diff --git a/README.md b/README.md
index f94286acf6..b0a1232944 100644
--- a/README.md
+++ b/README.md
@@ -232,16 +232,16 @@ jose.JWT.verify(
#### ID Token Verifying
ID Token is a JWT, but profiled, there are additional requirements to a JWT to be accepted as an
-ID Token and it is pretty easy to omit some, use the `profile` option of `JWT.verify` to make sure
-what you're accepting is really an ID Token meant to your Client. This will then perform all
-doable validations given the input. See the [documentation][documentation-jwt] for more.
+ID Token and it is pretty easy to omit some, use the `profile` option of `JWT.verify` or the
+`JWT.IdToken.verify` shorthand to make sure what you're accepting is really an ID Token meant to
+your Client. This will then perform all doable validations given the input. See the
+[documentation][documentation-jwt] for more.
```js
-jose.JWT.verify(
+jose.JWT.IdToken.verify(
'eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJzdWIiOiJmb28iLCJub25jZSI6ImE1MWNjZjA4ZjRiYmIwNmU4ODcxNWRkYzRiYmI0MWQ4IiwiYXVkIjoidXJuOmV4YW1wbGU6Y2xpZW50X2lkIiwiZXhwIjoxNTYzODg4ODMwLCJpYXQiOjE1NjM4ODUyMzAsImlzcyI6Imh0dHBzOi8vb3AuZXhhbXBsZS5jb20ifQ.RKCZczgICF5G9XdNDSwe4dolGauQHptpFKPzahA2wYGG2HKrKhyC8ZzqpeVc8cbntuqFBgABJVv6_9YICRx_dgwPYydTpZfZYjHnxrdWF9QsIPEGs672mrnhqIXUnXoseZ0TF6GOq6P7Qbf6gk1ru7TAbr_ieyJnNWcJhh5iHpz1k3mFz0TyTh7UNXshtQXftPUipqz4OBni5r9UaZXHw8B3QYOnms8__GJ3owOxaqkr1jgRs_EWqMlBNjPaj7ElVaeBWljDKuoK673tH0heSpgzUmUX_W8IDUVqs33uglpZwAQC7cAA5mGEg2odcRpvpP5M-WaP4RE9dl9jzcYmrw',
- keystore,
+ keyOrStore,
{
- profile: 'id_token',
issuer: 'https://op.example.com',
audience: 'urn:example:client_id',
nonce: 'a51ccf08f4bbb06e88715ddc4bbb41d8',
@@ -263,16 +263,16 @@ to validate those hashes after getting the ID Token payload and signature valida
When accepting a JWT-formatted OAuth 2.0 Access Token there are additional requirements for the JWT
to be accepted as an Access Token according to the [specification][draft-ietf-oauth-access-token-jwt]
-and it is pretty easy to omit some. Use the `profile` option of `JWT.verify` to make sure
-what you're accepting is really a JWT Access Token meant for your Resource Server. This will then
-perform all doable validations given the input. See the [documentation][documentation-jwt] for more.
+and it is pretty easy to omit some. Use the `profile` option of `JWT.verify` or the
+`JWT.AccessToken.verify` shorthand to make sure what you're accepting is really a JWT Access Token
+meant for your Resource Server. This will then perform all doable validations given the input. See
+the [documentation][documentation-jwt] for more.
```js
-jose.JWT.verify(
+jose.JWT.AccessToken.verify(
'eyJhbGciOiJQUzI1NiIsInR5cCI6ImF0K0pXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJzdWIiOiJmb28iLCJjbGllbnRfaWQiOiJ1cm46ZXhhbXBsZTpjbGllbnRfaWQiLCJhdWQiOiJ1cm46ZXhhbXBsZTpyZXNvdXJjZS1zZXJ2ZXIiLCJleHAiOjE1NjM4ODg4MzAsImlzcyI6Imh0dHBzOi8vb3AuZXhhbXBsZS5jb20iLCJzY29wZSI6ImFwaTpyZWFkIn0.UYy8vEGWS0cS24giCYobMMy9-bqI45p807yV1l-2WXX2J4UO-eohV_R58LE2oM88gl414c6XydO6QSYXul5roNPoOs41jpEvreQIP-HmegjbWGutktWJKfvoOblE5FjYwjrwStjLQGUzkq6KWcnDLPGmpFy7n6gZ4LF8YVz4dLEaO335hMNVNrmSPSXYqr7bAWybnLVpLxjDYwNfCO1g0_TlFx8fHh2OftHoOOmJFltFwb8JypkSB-JXVVSEh43IOEjeeMJIG_ylWIOxfLLi5Q7vPWgub83ZTkuGNe4KmlQJKIsH5k0yZSshsLYUOOH0RiXqQ-SA4Ubh3Fowigdu-g',
- keystore,
+ keyOrStore,
{
- profile: 'at+JWT',
issuer: 'https://op.example.com',
audience: 'urn:example:resource-server',
algorithms: ['PS256']
@@ -288,16 +288,16 @@ jose.JWT.verify(
#### Logout Token Verifying
Logout Token is a JWT, but profiled, there are additional requirements to a JWT to be accepted as an
-Logout Token and it is pretty easy to omit some, use the `profile` option of `JWT.verify` to make sure
-what you're accepting is really an Logout Token meant to your Client. This will then perform all
-doable validations given the input. See the [documentation][documentation-jwt] for more.
+Logout Token and it is pretty easy to omit some, use the `profile` option of `JWT.verify` or the
+`JWT.LogoutToken.verify` to make sure what you're accepting is really an Logout Token meant to your
+Client. This will then perform all doable validations given the input. See the
+[documentation][documentation-jwt] for more.
```js
-jose.JWT.verify(
+jose.JWT.LogoutToken.verify(
'eyJhbGciOiJQUzI1NiJ9.eyJzdWIiOiJmb28iLCJhdWQiOiJ1cm46ZXhhbXBsZTpjbGllbnRfaWQiLCJpYXQiOjE1NjM4ODg4MzAsImp0aSI6ImhqazMyN2RzYSIsImlzcyI6Imh0dHBzOi8vb3AuZXhhbXBsZS5jb20iLCJldmVudHMiOnsiaHR0cDovL3NjaGVtYXMub3BlbmlkLm5ldC9ldmVudC9iYWNrY2hhbm5lbC1sb2dvdXQiOnt9fX0.SBi7uNUvjHL9TFoFzautGgTQ1MjyeGUNYHL7inpgq3XgTv6xc9EAKuPRtpixmhdNhmInGwUvAeqDSJxomwv1KK1cTndrC9zAMZ7h657BGQAwGhu7nTm41fWMpKQdiLa9sqp3yit5_FNBmqUNeOoMPrYT_Vl9ytsoNO89MUQy2aqCd-Z7BrNJZH0QycdW6dmYlrmZL7w3t3TaAXoJDJ4Hgl2Itkkkb6_6gO-VoPIdVD8sDuf1zQzGhIkmcFrk0fXczVYOkeF2hNYBuvsM8LuO-EPA3oyE2In9djai3M7yceTQetRa1vwlqWkg_xmYS59ry-6wT44aN7-Y6p0TdXm-Zg',
- keystore,
+ keyOrStore,
{
- profile: 'logout_token',
issuer: 'https://op.example.com',
audience: 'urn:example:client_id',
algorithms: ['PS256']
diff --git a/docs/README.md b/docs/README.md
index 969381b673..5f60da5aa2 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -749,6 +749,9 @@ ks instanceof KeyStore
- [JWT.sign(payload, key[, options])](#jwtsignpayload-key-options)
- [JWT.verify(token, keyOrStore[, options])](#jwtverifytoken-keyorstore-options)
- [JWT.decode(token[, options])](#jwtdecodetoken-options)
+- [JWT.AccessToken.verify(token, keyOrStore, options)](#jwtaccesstokenverifytoken-keyorstore-options)
+- [JWT.IdToken.verify(token, keyOrStore, options)](#jwtidtokenverifytoken-keyorstore-options)
+- [JWT.LogoutToken.verify(token, keyOrStore, options)](#jwtlogouttokenverifytoken-keyorstore-options)
```js
@@ -932,6 +935,73 @@ JWT.decode(token, { complete: true })
---
+#### `JWT.AccessToken.verify(token, keyOrStore, options])`
+
+A shorthand for [`JWT.verify`](#jwtverifytoken-keyorstore-options) with the `profile` option set to `access_token`.
+
+
+Example (Click to expand)
+
+```js
+jose.JWT.AccessToken.verify(
+ 'eyJhbGciOiJQUzI1NiIsInR5cCI6ImF0K0pXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJzdWIiOiJmb28iLCJjbGllbnRfaWQiOiJ1cm46ZXhhbXBsZTpjbGllbnRfaWQiLCJhdWQiOiJ1cm46ZXhhbXBsZTpyZXNvdXJjZS1zZXJ2ZXIiLCJleHAiOjE1NjM4ODg4MzAsImlzcyI6Imh0dHBzOi8vb3AuZXhhbXBsZS5jb20iLCJzY29wZSI6ImFwaTpyZWFkIn0.UYy8vEGWS0cS24giCYobMMy9-bqI45p807yV1l-2WXX2J4UO-eohV_R58LE2oM88gl414c6XydO6QSYXul5roNPoOs41jpEvreQIP-HmegjbWGutktWJKfvoOblE5FjYwjrwStjLQGUzkq6KWcnDLPGmpFy7n6gZ4LF8YVz4dLEaO335hMNVNrmSPSXYqr7bAWybnLVpLxjDYwNfCO1g0_TlFx8fHh2OftHoOOmJFltFwb8JypkSB-JXVVSEh43IOEjeeMJIG_ylWIOxfLLi5Q7vPWgub83ZTkuGNe4KmlQJKIsH5k0yZSshsLYUOOH0RiXqQ-SA4Ubh3Fowigdu-g',
+ keyOrStore,
+ {
+ issuer: 'https://op.example.com',
+ audience: 'urn:example:resource-server',
+ algorithms: ['PS256']
+ }
+)
+```
+
+
+---
+
+#### `JWT.IdToken.verify(token, keyOrStore, options])`
+
+A shorthand for [`JWT.verify`](#jwtverifytoken-keyorstore-options) with the `profile` option set to `id_token`.
+
+
+Example (Click to expand)
+
+```js
+jose.JWT.IdToken.verify(
+ 'eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJzdWIiOiJmb28iLCJub25jZSI6ImE1MWNjZjA4ZjRiYmIwNmU4ODcxNWRkYzRiYmI0MWQ4IiwiYXVkIjoidXJuOmV4YW1wbGU6Y2xpZW50X2lkIiwiZXhwIjoxNTYzODg4ODMwLCJpYXQiOjE1NjM4ODUyMzAsImlzcyI6Imh0dHBzOi8vb3AuZXhhbXBsZS5jb20ifQ.RKCZczgICF5G9XdNDSwe4dolGauQHptpFKPzahA2wYGG2HKrKhyC8ZzqpeVc8cbntuqFBgABJVv6_9YICRx_dgwPYydTpZfZYjHnxrdWF9QsIPEGs672mrnhqIXUnXoseZ0TF6GOq6P7Qbf6gk1ru7TAbr_ieyJnNWcJhh5iHpz1k3mFz0TyTh7UNXshtQXftPUipqz4OBni5r9UaZXHw8B3QYOnms8__GJ3owOxaqkr1jgRs_EWqMlBNjPaj7ElVaeBWljDKuoK673tH0heSpgzUmUX_W8IDUVqs33uglpZwAQC7cAA5mGEg2odcRpvpP5M-WaP4RE9dl9jzcYmrw',
+ keyOrStore,
+ {
+ issuer: 'https://op.example.com',
+ audience: 'urn:example:client_id',
+ nonce: 'a51ccf08f4bbb06e88715ddc4bbb41d8',
+ algorithms: ['PS256']
+ }
+)
+```
+
+
+---
+
+#### `JWT.LogoutToken.verify(token, keyOrStore, options])`
+
+A shorthand for [`JWT.verify`](#jwtverifytoken-keyorstore-options) with the `profile` option set to `logout_token`.
+
+
+Example (Click to expand)
+
+```js
+jose.JWT.LogoutToken.verify(
+ 'eyJhbGciOiJQUzI1NiJ9.eyJzdWIiOiJmb28iLCJhdWQiOiJ1cm46ZXhhbXBsZTpjbGllbnRfaWQiLCJpYXQiOjE1NjM4ODg4MzAsImp0aSI6ImhqazMyN2RzYSIsImlzcyI6Imh0dHBzOi8vb3AuZXhhbXBsZS5jb20iLCJldmVudHMiOnsiaHR0cDovL3NjaGVtYXMub3BlbmlkLm5ldC9ldmVudC9iYWNrY2hhbm5lbC1sb2dvdXQiOnt9fX0.SBi7uNUvjHL9TFoFzautGgTQ1MjyeGUNYHL7inpgq3XgTv6xc9EAKuPRtpixmhdNhmInGwUvAeqDSJxomwv1KK1cTndrC9zAMZ7h657BGQAwGhu7nTm41fWMpKQdiLa9sqp3yit5_FNBmqUNeOoMPrYT_Vl9ytsoNO89MUQy2aqCd-Z7BrNJZH0QycdW6dmYlrmZL7w3t3TaAXoJDJ4Hgl2Itkkkb6_6gO-VoPIdVD8sDuf1zQzGhIkmcFrk0fXczVYOkeF2hNYBuvsM8LuO-EPA3oyE2In9djai3M7yceTQetRa1vwlqWkg_xmYS59ry-6wT44aN7-Y6p0TdXm-Zg',
+ keyOrStore,
+ {
+ issuer: 'https://op.example.com',
+ audience: 'urn:example:client_id',
+ algorithms: ['PS256']
+ }
+)
+```
+
+
+---
+
## JWS (JSON Web Signature)
diff --git a/lib/jwt/index.js b/lib/jwt/index.js
index 953b686acd..715ccd6f35 100644
--- a/lib/jwt/index.js
+++ b/lib/jwt/index.js
@@ -1,9 +1,11 @@
const decode = require('./decode')
const sign = require('./sign')
const verify = require('./verify')
+const profiles = require('./profiles')
module.exports = {
decode,
sign,
- verify
+ verify,
+ ...profiles
}
diff --git a/lib/jwt/profiles.js b/lib/jwt/profiles.js
new file mode 100644
index 0000000000..8ef18c4322
--- /dev/null
+++ b/lib/jwt/profiles.js
@@ -0,0 +1,7 @@
+const verify = require('./verify')
+
+module.exports = {
+ IdToken: { verify: (token, key, options) => verify(token, key, { ...options, profile: 'id_token' }) },
+ LogoutToken: { verify: (token, key, options) => verify(token, key, { ...options, profile: 'logout_token' }) },
+ AccessToken: { verify: (token, key, options) => verify(token, key, { ...options, profile: 'at+JWT' }) }
+}
diff --git a/test/jwt/verify.test.js b/test/jwt/verify.test.js
index 75fc7b9ee2..f7b0e77b29 100644
--- a/test/jwt/verify.test.js
+++ b/test/jwt/verify.test.js
@@ -364,18 +364,28 @@ test('must be a supported value', t => {
{
const token = JWT.sign({ }, key, { expiresIn: '10m', subject: 'subject', issuer: 'issuer', audience: 'client_id' })
+ test('profile=id_token', t => {
+ JWT.verify(token, key, { profile: 'id_token', issuer: 'issuer', audience: 'client_id' })
+ JWT.IdToken.verify(token, key, { issuer: 'issuer', audience: 'client_id' })
+ t.pass()
+ })
+
test('profile=id_token requires issuer option too', t => {
t.throws(() => {
JWT.verify(token, key, { profile: 'id_token' })
}, { instanceOf: TypeError, message: '"issuer" option is required to validate an ID Token' })
- JWT.verify(token, key, { profile: 'id_token', issuer: 'issuer', audience: 'client_id' })
+ t.throws(() => {
+ JWT.IdToken.verify(token, key)
+ }, { instanceOf: TypeError, message: '"issuer" option is required to validate an ID Token' })
})
test('profile=id_token requires audience option too', t => {
t.throws(() => {
JWT.verify(token, key, { profile: 'id_token', issuer: 'issuer' })
}, { instanceOf: TypeError, message: '"audience" option is required to validate an ID Token' })
- JWT.verify(token, key, { profile: 'id_token', issuer: 'issuer', audience: 'client_id' })
+ t.throws(() => {
+ JWT.IdToken.verify(token, key, { issuer: 'issuer' })
+ }, { instanceOf: TypeError, message: '"audience" option is required to validate an ID Token' })
})
test('profile=id_token mandates exp to be present', t => {
@@ -466,18 +476,28 @@ test('must be a supported value', t => {
}
}, key, { jti: 'foo', subject: 'subject', issuer: 'issuer', audience: 'client_id' })
+ test('profile=logout_token', t => {
+ JWT.verify(token, key, { profile: 'logout_token', issuer: 'issuer', audience: 'client_id' })
+ JWT.LogoutToken.verify(token, key, { issuer: 'issuer', audience: 'client_id' })
+ t.pass()
+ })
+
test('profile=logout_token requires issuer option too', t => {
t.throws(() => {
JWT.verify(token, key, { profile: 'logout_token' })
}, { instanceOf: TypeError, message: '"issuer" option is required to validate a Logout Token' })
- JWT.verify(token, key, { profile: 'logout_token', issuer: 'issuer', audience: 'client_id' })
+ t.throws(() => {
+ JWT.LogoutToken.verify(token, key)
+ }, { instanceOf: TypeError, message: '"issuer" option is required to validate a Logout Token' })
})
test('profile=logout_token requires audience option too', t => {
t.throws(() => {
JWT.verify(token, key, { profile: 'logout_token', issuer: 'issuer' })
}, { instanceOf: TypeError, message: '"audience" option is required to validate a Logout Token' })
- JWT.verify(token, key, { profile: 'logout_token', issuer: 'issuer', audience: 'client_id' })
+ t.throws(() => {
+ JWT.LogoutToken.verify(token, key, { issuer: 'issuer' })
+ }, { instanceOf: TypeError, message: '"audience" option is required to validate a Logout Token' })
})
test('profile=logout_token mandates jti to be present', t => {
@@ -602,18 +622,28 @@ test('must be a supported value', t => {
{
const token = JWT.sign({ client_id: 'client_id' }, key, { expiresIn: '10m', subject: 'subject', issuer: 'issuer', audience: 'RS', header: { typ: 'at+JWT' } })
+ test('profile=at+JWT', t => {
+ JWT.verify(token, key, { profile: 'at+JWT', issuer: 'issuer', audience: 'RS' })
+ JWT.AccessToken.verify(token, key, { issuer: 'issuer', audience: 'RS' })
+ t.pass()
+ })
+
test('profile=at+JWT requires issuer option too', t => {
t.throws(() => {
JWT.verify(token, key, { profile: 'at+JWT' })
}, { instanceOf: TypeError, message: '"issuer" option is required to validate a JWT Access Token' })
- JWT.verify(token, key, { profile: 'at+JWT', issuer: 'issuer', audience: 'RS' })
+ t.throws(() => {
+ JWT.AccessToken.verify(token, key)
+ }, { instanceOf: TypeError, message: '"issuer" option is required to validate a JWT Access Token' })
})
test('profile=at+JWT requires audience option too', t => {
t.throws(() => {
JWT.verify(token, key, { profile: 'at+JWT', issuer: 'issuer' })
}, { instanceOf: TypeError, message: '"audience" option is required to validate a JWT Access Token' })
- JWT.verify(token, key, { profile: 'at+JWT', issuer: 'issuer', audience: 'RS' })
+ t.throws(() => {
+ JWT.AccessToken.verify(token, key, { issuer: 'issuer' })
+ }, { instanceOf: TypeError, message: '"audience" option is required to validate a JWT Access Token' })
})
test('profile=at+JWT mandates exp to be present', t => {
diff --git a/types/index.d.ts b/types/index.d.ts
index f9727f92bb..6768428df6 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -381,7 +381,29 @@ export namespace JWT {
nonce?: string;
now?: Date;
}
+
function sign(payload: object, key: ProduceKeyInput, options?: SignOptions): string;
+
+ interface VerifyProfileOptions {
+ issuer: string;
+ audience: string | string[];
+ profile?: profile;
+ }
+
+ namespace IdToken {
+ function verify(jwt: string, key: ConsumeKeyInput, options: VerifyOptions & VerifyProfileOptions<'id_token'>): object;
+ function verify(jwt: string, key: ConsumeKeyInput, options: VerifyOptions & VerifyProfileOptions<'id_token'>): completeResult;
+ }
+
+ namespace LogoutToken {
+ function verify(jwt: string, key: ConsumeKeyInput, options: VerifyOptions & VerifyProfileOptions<'logout_token'>): object;
+ function verify(jwt: string, key: ConsumeKeyInput, options: VerifyOptions & VerifyProfileOptions<'logout_token'>): completeResult;
+ }
+
+ namespace AccessToken {
+ function verify(jwt: string, key: ConsumeKeyInput, options: VerifyOptions & VerifyProfileOptions<'at+JWT'>): object;
+ function verify(jwt: string, key: ConsumeKeyInput, options: VerifyOptions & VerifyProfileOptions<'at+JWT'>): completeResult;
+ }
}
export namespace errors {