WIP jwk get and verify
This commit is contained in:
		
							parent
							
								
									2587d03860
								
							
						
					
					
						commit
						69ce9bf95f
					
				
							
								
								
									
										149
									
								
								oauth3.core.js
									
									
									
									
									
								
							
							
						
						
									
										149
									
								
								oauth3.core.js
									
									
									
									
									
								
							| @ -17,6 +17,13 @@ | |||||||
|         err.code = params.error.code || params.error; |         err.code = params.error.code || params.error; | ||||||
|         return err; |         return err; | ||||||
|       } |       } | ||||||
|  |     , create: function (opts) { | ||||||
|  |         var err = new Error(opts.message); | ||||||
|  |         err.code = opts.code; | ||||||
|  |         err.uri = opts.uri || opts.url; | ||||||
|  |         err.subErr = opts.subErr; | ||||||
|  |         return err; | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   , _binStr: { |   , _binStr: { | ||||||
|       bufferToBinStr: function (buf) { |       bufferToBinStr: function (buf) { | ||||||
| @ -205,6 +212,75 @@ | |||||||
|       } |       } | ||||||
|       return str; |       return str; | ||||||
|     } |     } | ||||||
|  |   , jwk: { | ||||||
|  |       get: function (decoded) { | ||||||
|  |         return OAUTH3.discover(decoded.payload.iss).then(function (directives) { | ||||||
|  |           var urlObj = OAUTH3.jwk.url(directives, decoded); | ||||||
|  |           return OAUTH3.request(urlObj).catch(function (err) { | ||||||
|  |             return PromiseA.reject({ | ||||||
|  |               message: 'failed to retrieve public key from token issuer' | ||||||
|  |             , code: 'E_NO_PUB_KEY' | ||||||
|  |             , url: 'https://oauth3.org/docs/errors#E_NO_PUB_KEY' | ||||||
|  |             , subErr: err.toString() | ||||||
|  |             }); | ||||||
|  |           }); | ||||||
|  |         }, function (err) { | ||||||
|  |           return PromiseA.reject({ | ||||||
|  |             message: 'token issuer is not a valid OAuth3 provider' | ||||||
|  |           , code: 'E_INVALID_ISS' | ||||||
|  |           , url: 'https://oauth3.org/docs/errors#E_INVALID_ISS' | ||||||
|  |           , subErr: err.toString() | ||||||
|  |           }); | ||||||
|  |         }).then(function (res) { | ||||||
|  |           if (res.data.error) { | ||||||
|  |             return PromiseA.reject(res.data.error); | ||||||
|  |           } | ||||||
|  |           return res.data; | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |     , verifyToken: function (token) { | ||||||
|  |         var decoded; | ||||||
|  | 
 | ||||||
|  |         if (!token) { | ||||||
|  |           return PromiseA.reject({ | ||||||
|  |             message: 'no token provided' | ||||||
|  |           , code: 'E_NO_TOKEN' | ||||||
|  |           , url: 'https://oauth3.org/docs/errors#E_NO_TOKEN' | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |           decoded = OAUTH3.jwt.decode(token, {complete: true}); | ||||||
|  |         } catch (e) {} | ||||||
|  |         if (!decoded) { | ||||||
|  |           return PromiseA.reject({ | ||||||
|  |             message: 'provided token not a JSON Web Token' | ||||||
|  |           , code: 'E_NOT_JWT' | ||||||
|  |           , url: 'https://oauth3.org/docs/errors#E_NOT_JWT' | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return OAUTH3.jwk.get(decoded).then(function (jwk) { | ||||||
|  |           var opts = {}; | ||||||
|  |           if (Array.isArray(jwk.alg)) { | ||||||
|  |             opts.algorithms = jwk.alg; | ||||||
|  |           } else if (typeof jwk.alg === 'string') { | ||||||
|  |             opts.algorithms = [ jwk.alg ]; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           try { | ||||||
|  |             return OAUTH3.jwt.verify(token, jwk, opts); | ||||||
|  |           } catch (err) { | ||||||
|  |             return PromiseA.reject({ | ||||||
|  |               message: 'token verification failed' | ||||||
|  |             , code: 'E_INVALID_TOKEN' | ||||||
|  |             , url: 'https://oauth3.org/docs/errors#E_INVALID_TOKEN' | ||||||
|  |             , subErr: err.toString() | ||||||
|  |             }); | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|   , jwt: { |   , jwt: { | ||||||
|       // decode only (no verification)
 |       // decode only (no verification)
 | ||||||
|       decode: function (token, opts) { |       decode: function (token, opts) { | ||||||
| @ -228,7 +304,7 @@ | |||||||
|         , payload: JSON.parse(OAUTH3._base64.decodeUrlSafe(parts[1])) |         , payload: JSON.parse(OAUTH3._base64.decodeUrlSafe(parts[1])) | ||||||
|         }; |         }; | ||||||
|       } |       } | ||||||
|     , verify: function (token, jwk) { |     , verify: function (token, jwk/*, opts*/) { | ||||||
|         if (!OAUTH3.crypto) { |         if (!OAUTH3.crypto) { | ||||||
|           return OAUTH3.PromiseA.reject(new Error("OAuth3 crypto library unavailable")); |           return OAUTH3.PromiseA.reject(new Error("OAuth3 crypto library unavailable")); | ||||||
|         } |         } | ||||||
| @ -237,9 +313,15 @@ | |||||||
|         var parts = token.split(/\./g); |         var parts = token.split(/\./g); | ||||||
|         var data = OAUTH3._binStr.binStrToBuffer(parts.slice(0, 2).join('.')); |         var data = OAUTH3._binStr.binStrToBuffer(parts.slice(0, 2).join('.')); | ||||||
|         var signature = OAUTH3._base64.urlSafeToBuffer(parts[2]); |         var signature = OAUTH3._base64.urlSafeToBuffer(parts[2]); | ||||||
|  |         var decoded = OAUTH3.jwt.decode(token, { complete: true }); | ||||||
| 
 | 
 | ||||||
|  |         // TODO disallow none and hmac algorithms
 | ||||||
|  |         // https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/
 | ||||||
|  |         if (!decoded.header.alg || 'none' === decoded.header.alg.toString() || /^HS/i.test(decoded.header.alg.toString())) { | ||||||
|  |           throw new Error("token algorithm '" + decoded.header.alg + "' is not accepted"); | ||||||
|  |         } | ||||||
|         return OAUTH3.crypto.core.verify(jwk, data, signature).then(function () { |         return OAUTH3.crypto.core.verify(jwk, data, signature).then(function () { | ||||||
|           return OAUTH3.jwt.decode(token); |           return decoded; | ||||||
|         }); |         }); | ||||||
|       } |       } | ||||||
|     , sign: function (payload, jwk) { |     , sign: function (payload, jwk) { | ||||||
| @ -335,6 +417,69 @@ | |||||||
|         opts._pathname = ".well-known/oauth3/scopes.json"; |         opts._pathname = ".well-known/oauth3/scopes.json"; | ||||||
|         return OAUTH3.urls.rpc(providerUri, opts); |         return OAUTH3.urls.rpc(providerUri, opts); | ||||||
|       } |       } | ||||||
|  |     , jwk: function (directives, decoded) { | ||||||
|  |         var sub = decoded.payload.sub || decoded.payload.ppid || decoded.payload.appScopedId; | ||||||
|  |         if (!sub) { | ||||||
|  |           throw OAUTH3.error.create({ | ||||||
|  |             message: 'token missing sub' | ||||||
|  |           , code: 'E_MISSING_SUB' | ||||||
|  |           , url: 'https://oauth3.org/docs/errors#E_MISSING_SUB' | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  |         var kid = decoded.header.kid || decoded.payload.kid; | ||||||
|  |         if (!kid) { | ||||||
|  |           throw OAUTH3.error.create({ | ||||||
|  |             message: 'token missing kid' | ||||||
|  |           , code: 'E_MISSING_KID' | ||||||
|  |           , url: 'https://oauth3.org/docs/errors#E_MISSING_KID' | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  |         if (!decoded.payload.iss) { | ||||||
|  |           throw OAUTH3.error.create({ | ||||||
|  |             message: 'token missing iss' | ||||||
|  |           , code: 'E_MISSING_ISS' | ||||||
|  |           , url: 'https://oauth3.org/docs/errors#E_MISSING_ISS' | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         var args = (directives || {}).retrieve_jwk; | ||||||
|  |         if (typeof args === 'string') { | ||||||
|  |           args = { url: args, method: 'GET' }; | ||||||
|  |         } | ||||||
|  |         if (typeof (args || {}).url !== 'string') { | ||||||
|  |           throw OAUTH3.error.create({ | ||||||
|  |             message: 'token issuer does not support retrieving JWKs' | ||||||
|  |           , code: 'E_INVALID_ISS' | ||||||
|  |           , url: 'https://oauth3.org/docs/errors#E_INVALID_ISS' | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         var params = { | ||||||
|  |           sub: sub | ||||||
|  |         , kid: kid | ||||||
|  |         }; | ||||||
|  |         var url = args.url; | ||||||
|  |         var body; | ||||||
|  |         Object.keys(params).forEach(function (key) { | ||||||
|  |           if (url.indexOf(':'+key) !== -1) { | ||||||
|  |             url = url.replace(':'+key, params[key]); | ||||||
|  |             delete params[key]; | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  |         if (Object.keys(params).length > 0) { | ||||||
|  |           if ('GET' === (args.method || 'GET').toUpperCase()) { | ||||||
|  |             url += '?' + OAUTH3.query.stringify(params); | ||||||
|  |           } else { | ||||||
|  |             body = params; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return { | ||||||
|  |           url: OAUTH3.url.resolve(directives.api, url) | ||||||
|  |         , method: args.method | ||||||
|  |         , data: body | ||||||
|  |         }; | ||||||
|  |       } | ||||||
|     , implicitGrant: function (directive, opts) { |     , implicitGrant: function (directive, opts) { | ||||||
|         //
 |         //
 | ||||||
|         // Example Implicit Grant Request
 |         // Example Implicit Grant Request
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user