1 |
|
2 |
|
3 |
|
4 |
|
5 | var JWT = require('anvil-connect-jwt')
|
6 | var IDTokenError = require('./IDTokenError')
|
7 | var nowSeconds = require('./time-utils').nowSeconds
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | function expires (duration) {
|
14 | var fromNow = {
|
15 | day: (1000 * 60 * 60 * 24),
|
16 | week: (1000 * 60 * 60 * 24 * 7),
|
17 | month: (1000 * 60 * 60 * 24 * 30)
|
18 | }
|
19 |
|
20 | return function () {
|
21 | return nowSeconds(fromNow[duration])
|
22 | }
|
23 | }
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 | var IDToken = JWT.define({
|
30 |
|
31 | header: {
|
32 | alg: 'RS256'
|
33 | },
|
34 |
|
35 |
|
36 | headers: [
|
37 | 'alg'
|
38 | ],
|
39 |
|
40 |
|
41 | registeredHeaders: {
|
42 | alg: { format: 'StringOrURI', required: true, enum: ['RS256'] }
|
43 | },
|
44 |
|
45 |
|
46 | claims: ['iss', 'sub', 'aud', 'exp', 'iat', 'nonce', 'acr', 'at_hash'],
|
47 |
|
48 |
|
49 | registeredClaims: {
|
50 | iss: { format: 'StringOrURI', required: true },
|
51 | sub: { format: 'StringOrURI', required: true },
|
52 | aud: { format: 'StringOrURI', required: true },
|
53 | exp: { format: 'IntDate', required: true, default: expires('day') },
|
54 | iat: { format: 'IntDate', required: true, default: nowSeconds },
|
55 | nonce: { format: 'String' },
|
56 | acr: { format: 'String' },
|
57 | at_hash: { format: 'String' }
|
58 | }
|
59 |
|
60 | })
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 | IDToken.verify = function (jwt, options, callback) {
|
67 | var token = IDToken.decode(jwt, options.key)
|
68 |
|
69 | if (!token || token instanceof Error) {
|
70 | return callback(new IDTokenError({
|
71 | error: 'Invalid JWT'
|
72 | }))
|
73 | }
|
74 |
|
75 | var header = token.header
|
76 | var claims = token.payload
|
77 | var alg = options.alg || 'RS256'
|
78 |
|
79 |
|
80 | if (claims.iss !== options.iss) {
|
81 | return callback(new IDTokenError({
|
82 | error: 'Mismatching issuer'
|
83 | }))
|
84 | }
|
85 |
|
86 |
|
87 | if (claims.aud.indexOf(options.aud) === -1) {
|
88 | return callback(new IDTokenError({
|
89 | error: 'Mismatching audience'
|
90 | }))
|
91 | }
|
92 |
|
93 |
|
94 | if (header.alg !== alg) {
|
95 | return callback(new IDTokenError({
|
96 | error: 'Expected ' + alg + ' signature'
|
97 | }))
|
98 | }
|
99 |
|
100 |
|
101 | if (claims.exp < nowSeconds()) {
|
102 | return callback(new IDTokenError({
|
103 | error: 'Expired token'
|
104 | }))
|
105 | }
|
106 |
|
107 | callback(null, token)
|
108 | }
|
109 |
|
110 |
|
111 |
|
112 |
|
113 |
|
114 | module.exports = IDToken
|