/cognito.js Secret
Last active
November 21, 2017 21:35
Star
You must be signed in to star a gist
AWS Cognito Auth for Realm Object Server
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
"use strict"; | |
var jwt = require('jsonwebtoken'); | |
var request = require('request'); | |
var jwkToPem = require('jwk-to-pem'); | |
/** | |
* This will be called when the server is started. | |
* | |
* It should return the constructor for the authentication provider. | |
* | |
* @param {object} deps - The dependencies passed from the running server | |
* to this implementation. | |
* @param {function} deps.BaseAuthProvider - the base class to use | |
* @param {object} deps.problem - a set of exception to throw on failure | |
* @param {object} deps.models - the models of the admin-Realm | |
* @returns {function} | |
*/ | |
module.exports = function(deps) { | |
return class CognitoAuthProvider extends deps.BaseAuthProvider { | |
// This is used by the ROS when it loads the available | |
// authentication providers. This function is required. | |
static get name() { | |
return 'custom/cognito'; | |
} | |
// This is used by ROS when it parses the configuration | |
// file, to ensure that required default options are | |
// available. This function is optional. | |
static get defaultOptions() { | |
return { | |
iss: undefined, | |
} | |
} | |
constructor(name, options, requestPromise) { | |
super(name, options, requestPromise); | |
this.pems = undefined; | |
if (!this.options.iss) { | |
throw new deps.problem.RealmProblem.ServerMisConfiguration({ | |
detail: 'Missing cognito configuration key: iss', | |
}); | |
} | |
} | |
obtainPems() { | |
if (!this.pems) { | |
// Download the JWKs and save it as PEM | |
const httpOptions = { | |
uri: `${this.options.iss}/.well-known/jwks.json`, | |
method: 'GET', | |
json: true, | |
}; | |
return this.request(httpOptions) | |
.catch((err) => { | |
throw new deps.problem.HttpProblem.Unauthorized({ | |
detail: `Unable to retrieve PEMs from Cognito: ${err.toString()}`, | |
}); | |
}) | |
.then((result) => { | |
this.pems = {}; | |
var keys = result.keys; | |
for (var i = 0; i < keys.length; i++) { | |
// Convert each key to PEM | |
var key_id = keys[i].kid; | |
var modulus = keys[i].n; | |
var exponent = keys[i].e; | |
var key_type = keys[i].kty; | |
var jwk = { kty: key_type, n: modulus, e: exponent}; | |
var pem = jwkToPem(jwk); | |
this.pems[key_id] = pem; | |
} | |
return this.pems; | |
}); | |
} | |
else { | |
return Promise.resolve(this.pems); | |
} | |
} | |
verifyIdentifier(req) { | |
// The token submitted by the client | |
const token = req.body.data; | |
return this.obtainPems() | |
.then((pems) => { | |
// Fail if the token is not jwt | |
var decodedJwt = jwt.decode(token, { | |
complete: true | |
}); | |
if (!decodedJwt) { | |
throw new deps.problem.HttpProblem.Unauthorized({ | |
detail: 'The token sent by the client was not a valid JWT', | |
}); | |
} | |
// Fail if token is not from your User Pool | |
if (decodedJwt.payload.iss != this.options.iss) { | |
throw new deps.problem.HttpProblem.Unauthorized({ | |
detail: 'The token sent by the client was issued by the correct ISS', | |
}); | |
} | |
// Reject the jwt if it's not an 'Access Token' | |
if (decodedJwt.payload.token_use != 'access') { | |
throw new deps.problem.HttpProblem.Unauthorized({ | |
detail: 'The token sent by the client is not a valid access token', | |
}); | |
} | |
// Get the keyId from the token and retrieve corresponding PEM | |
var keyId = decodedJwt.header.kid; | |
var pem = pems[keyId]; | |
if (!pem) { | |
throw new deps.problem.HttpProblem.Unauthorized({ | |
detail: 'The token sent by the client is not a valid access token', | |
}); | |
} | |
// Verify the signature of the JWT token to ensure it's really coming from your User Pool | |
return new Promise((res, rej) => { | |
jwt.verify(token, pem, { | |
issuer: this.options.iss | |
}, (err, payload) => { | |
if (err) { | |
rej(err); | |
} | |
else { | |
res(decodedJwt.payload.username); | |
} | |
}); | |
}); | |
}); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment