-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 448d159
Showing
36 changed files
with
1,648 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
{ | ||
"array.empty_padding": true, | ||
"array.multiline.element_count": 4, | ||
"array.multiline.leading_comma": false, | ||
"array.multiline.leading_comma.padding": true, | ||
"array.multiline.min_length": 80, | ||
"array.padding": true, | ||
"binary_operators.padding": true, | ||
"brackets.padding": true, | ||
"comment.asterisks": "indent", | ||
"for_loop_semicolons.padding": true, | ||
"function_anonymous.empty_padding": false, | ||
"function_anonymous.group_to_block_spacing": "spaced", | ||
"function_anonymous.multiline.element_count": 4, | ||
"function_anonymous.multiline.leading_comma": false, | ||
"function_anonymous.multiline.leading_comma.padding": true, | ||
"function_anonymous.multiline.min_length": 40, | ||
"function_anonymous.padding": true, | ||
"function_call.empty_padding": false, | ||
"function_call.multiline.element_count": 4, | ||
"function_call.multiline.leading_comma": false, | ||
"function_call.multiline.leading_comma.padding": true, | ||
"function_call.multiline.min_length": 40, | ||
"function_call.padding": true, | ||
"function_declaration.empty_padding": false, | ||
"function_declaration.group_to_block_spacing": "spaced", | ||
"function_declaration.multiline.element_count": 4, | ||
"function_declaration.multiline.leading_comma": false, | ||
"function_declaration.multiline.leading_comma.padding": true, | ||
"function_declaration.multiline.min_length": 40, | ||
"function_declaration.padding": true, | ||
"indent_size": 4, | ||
"keywords.block_to_keyword_spacing": "spaced", | ||
"keywords.empty_group_spacing": false, | ||
"keywords.group_to_block_spacing": "spaced", | ||
"keywords.padding_inside_group": true, | ||
"keywords.spacing_to_block": "spaced", | ||
"keywords.spacing_to_group": true, | ||
"max_columns": 120, | ||
"parentheses.padding": true, | ||
"strings.attributes.quote": "double", | ||
"strings.quote": "single", | ||
"struct.empty_padding": true, | ||
"struct.multiline.element_count": 0, | ||
"struct.multiline.leading_comma": false, | ||
"struct.multiline.leading_comma.padding": true, | ||
"struct.multiline.min_length": 0, | ||
"struct.padding": true, | ||
"struct.separator": ": ", | ||
"tab_indent": false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# Set the default behavior, in case people don't have core.autocrlf set. | ||
* text=auto |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
testbox/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright (c) 2019 | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
component { | ||
|
||
this.title = 'jwt-cfml'; | ||
this.author = 'John Berquist'; | ||
this.webURL = 'https://github.com/jcberquist/jwt-cfml'; | ||
this.description = 'This module supports encoding and decoding JSON Web Tokens.'; | ||
|
||
function configure() {} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
# jwt-cfml | ||
|
||
**jwt-cfml** is a CFML (Lucee and ColdFusion) library for encoding and decoding JSON Web Tokens. | ||
|
||
It supports the following algorithms: | ||
|
||
- HS256 | ||
- HS384 | ||
- HS512 | ||
- RS256 | ||
- RS384 | ||
- RS512 | ||
- ES256 | ||
- ES384 | ||
- ES512 | ||
|
||
In the case of the `RS` and `ES` algorithms, asymmetric keys are expected to be provided in unencrypted PEM or JWK format (in the latter case first deserialize the JWK to a CFML struct). When use PEM, private keys need to be encoded in PKCS#8 format. | ||
|
||
If your private key is not currently in this format, conversion should be straightforward: | ||
|
||
```bash | ||
$ openssl pkcs8 -topk8 -nocrypt -in privatekey.pem -out privatekey.pk8 | ||
``` | ||
|
||
When decoding tokens, either a public key or certificate can be provided. (If a certificate is provided, the public key will be extracted from it.) | ||
|
||
*You can pre-parse your encoded keys and pass the returned Java classes to the `encode()` and `decode()` methods, to avoid having them parsed on every method call. See [Parsing Asymmetric Keys](https://github.com/jcberquist/jwt-cfml-dev/blob/master/README.md#parsing-asymmetric-keys) below.* | ||
|
||
## Installation | ||
|
||
Installation is done via CommandBox: | ||
|
||
```bash | ||
$ box install jwt-cfml | ||
``` | ||
|
||
`jwt-cfml` will be installed into a `jwtcfml` package directory by default. | ||
|
||
*Alternatively the git repository can be cloned into the desired directory.* | ||
|
||
### Standalone | ||
|
||
Once the library has been installed, the core `jwt` component can be instantiated directly: | ||
|
||
```cfc | ||
jwt = new path.to.jwtcfml.models.jwt(); | ||
``` | ||
|
||
### ColdBox Module | ||
|
||
You can make use of the library via the injection DSL: `jwt@jwtcfml` | ||
|
||
## Usage | ||
|
||
### Encoding tokens: | ||
|
||
```cfc | ||
payload = {'key': 'value'}; | ||
secret = 'secret'; | ||
token = jwt.encode(payload, secret, 'HS256'); | ||
``` | ||
|
||
```cfc | ||
pemPrivateKey = ' | ||
-----BEGIN PRIVATE KEY----- | ||
... | ||
-----END PRIVATE KEY----- | ||
'; | ||
token = jwt.encode(payload, pemPrivateKey, 'RS256'); | ||
``` | ||
|
||
```cfc | ||
jwk = { | ||
"alg": "RS256", | ||
"d": "...", | ||
"dp": "...", | ||
"dq": "...", | ||
"e": "AQAB", | ||
"kty": "RSA", | ||
"n": "...", | ||
"p": "...", | ||
"q": "...", | ||
"qi": "..." | ||
}; | ||
token = jwt.encode(payload, jwk, 'RS256'); | ||
``` | ||
|
||
When a token is encoded, a header is automatically included containing | ||
`"typ"` set to `"JWT"` and `"alg"` set to the passed in algorithm. If you | ||
need to add additional headers a fourth argument, `headers`, is available | ||
for this: | ||
|
||
```cfc | ||
token = jwt.encode(payload, pemPrivateKey, 'RS256', {'kid': 'abc123'}); | ||
``` | ||
|
||
If your token payload contains `"iat"`, `"exp"`, or `"nbf"` claims, you can | ||
set these to CFML date objects, and they will automatically be converted to | ||
UNIX timestamps in the generated token for you. | ||
|
||
```cfc | ||
payload = {'iat': now()}; | ||
token = jwt.encode(payload, secret, 'HS256'); | ||
``` | ||
|
||
### Decoding tokens: | ||
|
||
```cfc | ||
token = 'eyJ0e...'; | ||
secret = 'secret'; | ||
payload = jwt.decode(token, secret, 'HS256'); | ||
``` | ||
|
||
```cfc | ||
token = 'eyJ0e...'; | ||
pemPublicKey = ' | ||
-----BEGIN PUBLIC KEY----- | ||
... | ||
-----END PUBLIC KEY----- | ||
'; | ||
payload = jwt.decode(token, pemPublicKey, 'RS256'); | ||
``` | ||
|
||
```cfc | ||
token = 'eyJ0e...'; | ||
pemCertificate = ' | ||
-----BEGIN CERTIFICATE----- | ||
... | ||
-----END CERTIFICATE----- | ||
'; | ||
payload = jwt.decode(token, pemCertificate, 'RS256'); | ||
``` | ||
|
||
```cfc | ||
token = 'eyJ0e...'; | ||
jwk = { | ||
"e": "AQAB", | ||
"kty": "RSA", | ||
"alg": "RS256", | ||
"n": "...", | ||
"use": "sig" | ||
} | ||
payload = jwt.decode(token, jwk, 'RS256'); | ||
``` | ||
|
||
*Note: This library does not rely solely on the algorithm specified in the token header. You **must** specify the allowed algorithms (either as a string or an array) when calling `decode()`. The algorithm in the token header must match one of the allowed algorithms.* | ||
|
||
If the decoded payload contains `"iat"`, `"exp"`, or `"nbf"` claims, they will be automatically converted from UNIX timestamps to CFML date objects for you. | ||
|
||
#### Getting the token header | ||
|
||
If you need to get the token header before decoding (e.g. you need a `"kid"` from it), you can use the `jwt.getHeader()` method. This will return the token header as a struct. | ||
|
||
```cfc | ||
token = 'eyJ0e...'; | ||
header = jwt.getHeader(token); | ||
``` | ||
|
||
#### Token validity | ||
|
||
If a token signature is invalid, the `jwt.decode()` method will throw an error. Further, if the payload contains a `"exp"` or `"nbf"` claim these will be verified as well. | ||
|
||
If you also wish to verify an audience or issuer claim, you can pass valid claims into the decode method: | ||
|
||
```cfc | ||
claims = { | ||
"iss": "somissuer", | ||
"aud": "someaudience" // this can also be an array | ||
}; | ||
payload = jwt.decode(token, pemCertificate, 'RS256', claims); | ||
``` | ||
|
||
This argument can also be used to ignore the `"exp"` and `"nbf"` claims or to validate them against a timestamp other than the current time: | ||
|
||
```cfc | ||
claims = { | ||
// `exp` will be validated against 1 min in the past instead of the current time | ||
"exp": dateAdd('n', -1, now()), | ||
// `nbf` will be ignored | ||
"nbf": false | ||
}; | ||
payload = jwt.decode(token, pemCertificate, 'RS256', claims); | ||
``` | ||
|
||
#### Unverified Payload | ||
|
||
If you need to get the payload without doing any verification at all you can pass `verify=false` into the decode method: | ||
|
||
```cfc | ||
jwt.decode(token = token, verify = false); | ||
``` | ||
|
||
### Parsing Asymmetric Keys | ||
|
||
Every time a PEM key or JWK is passed into `encode()` and `decode()` it must be converted to binary data and then the appropriate Java class created. You can avoid this (minor) overhead by parsing your key upfront, and then passing the generated Java key class directly into `encode()` and `decode()`: | ||
|
||
```cfc | ||
pemCertificate = ' | ||
-----BEGIN CERTIFICATE----- | ||
... | ||
-----END CERTIFICATE----- | ||
'; | ||
publicKey = jwt.parsePEMEncodedKey(pemCertificate); | ||
payload = jwt.decode(token, publicKey, 'RS256'); | ||
``` | ||
|
||
```cfc | ||
jwk = { | ||
"e": "AQAB", | ||
"kty": "RSA", | ||
"alg": "RS256", | ||
"n": "AN...", | ||
"use": "sig" | ||
}; | ||
publicKey = jwt.parseJWK(jwk); | ||
payload = jwt.decode(token, publicKey, 'RS256'); | ||
``` | ||
|
||
### Acknowledgments | ||
|
||
- <https://github.com/jpadilla/pyjwt> | ||
- <https://github.com/jwtk/jjwt> | ||
- <https://github.com/apache/cxf> | ||
- <https://bitbucket.org/b_c/jose4j> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
{ | ||
"name":"JWT CFML", | ||
"version":"1.0.0", | ||
"author":"John Berquist", | ||
"location":"forgeboxStorage", | ||
"homepage":"https://github.com/jcberquist/jwt-cfml", | ||
"documentation":"", | ||
"repository":{ | ||
"type":"", | ||
"URL":"" | ||
}, | ||
"bugs":"", | ||
"slug":"jwt-cfml", | ||
"packageDirectory": "jwtcfml", | ||
"shortDescription":"JWT CFML is a CFML (Lucee and ColdFusion) library for using JSON Web Tokens.", | ||
"description":"", | ||
"instructions":"", | ||
"changelog":"", | ||
"type":"modules", | ||
"keywords":[ | ||
"jwt" | ||
], | ||
"private":false, | ||
"projectURL":"", | ||
"license":[ | ||
{ | ||
"type":"MIT", | ||
"URL":"" | ||
} | ||
], | ||
"contributors":[], | ||
"dependencies":{}, | ||
"devDependencies":{ | ||
"testbox":"^3.0.0" | ||
}, | ||
"installPaths":{ | ||
"testbox":"testbox/" | ||
}, | ||
"scripts": { | ||
"tests": "start tests/server.json && start tests/server-2018.json && start tests/server-2016.json", | ||
"format": "cfformat models,tests --overwrite" | ||
} | ||
} |
Oops, something went wrong.