From 9334e7c5b3bb37677b958e2f196fdf105c2255db Mon Sep 17 00:00:00 2001 From: Luciano Date: Sat, 7 Sep 2019 23:00:34 +0100 Subject: [PATCH 1/3] Implemented decorator getNewAccessTokenWithRefreshToken --- README.md | 3 +++ index.js | 14 ++++++++++++++ test.js | 34 ++++++++++++++++++++++++++-------- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 714c862..4975c2a 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,9 @@ fastify.get('/login/facebook/callback', async function (request, reply) { console.log(result.access_token) + // if later you need to refresh the token you can use + // this.getNewAccessTokenWithRefreshToken(result.refresh_token) + reply.send({ access_token: result.access_token }) }) ``` diff --git a/index.js b/index.js index b87a95a..e2a1ab8 100644 --- a/index.js +++ b/index.js @@ -94,11 +94,25 @@ const oauthPlugin = fp(function (fastify, options, next) { getAccessTokenFromAuthorizationCodeFlowCallbacked(request, callback) } + function getNewAccessTokenWithRefreshTokenCallbacked (refreshToken, params, callback) { + const accessToken = fastify[name].accessToken.create({ refresh_token: refreshToken }) + accessToken.refresh(params, callback) + } + const getNewAccessTokenWithRefreshTokenPromisified = promisify(getNewAccessTokenWithRefreshTokenCallbacked) + + function getNewAccessTokenWithRefreshToken (refreshToken, params, callback) { + if (!callback) { + return getNewAccessTokenWithRefreshTokenPromisified(refreshToken, params) + } + getNewAccessTokenWithRefreshTokenCallbacked(refreshToken, params, callback) + } + const oauth2 = oauth2Module.create(credentials) if (startRedirectPath) { fastify.get(startRedirectPath, startRedirectHandler) fastify.decorate('getAccessTokenFromAuthorizationCodeFlow', getAccessTokenFromAuthorizationCodeFlow) + fastify.decorate('getNewAccessTokenWithRefreshToken', getNewAccessTokenWithRefreshToken) } try { diff --git a/test.js b/test.js index b93ff2d..3957ad1 100644 --- a/test.js +++ b/test.js @@ -31,9 +31,18 @@ function makeRequests (t, fastify) { expires_in: '240000' } + const RESPONSE_BODY_REFRESHED = { + access_token: 'my-access-token-refreshed', + refresh_token: 'my-refresh-token-refreshed', + token_type: 'bearer', + expires_in: '240000' + } + const githubScope = nock('https://github.com') .post('/login/oauth/access_token', 'code=my-code&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fcallback&grant_type=authorization_code&client_id=my-client-id&client_secret=my-secret') .reply(200, RESPONSE_BODY) + .post('/login/oauth/access_token', 'grant_type=refresh_token&refresh_token=my-refresh-token&client_id=my-client-id&client_secret=my-secret') + .reply(200, RESPONSE_BODY_REFRESHED) fastify.inject({ method: 'GET', @@ -42,7 +51,7 @@ function makeRequests (t, fastify) { t.error(err) t.equal(responseEnd.statusCode, 200) - t.strictSame(JSON.parse(responseEnd.payload), RESPONSE_BODY) + t.strictSame(JSON.parse(responseEnd.payload), RESPONSE_BODY_REFRESHED) githubScope.done() @@ -74,12 +83,18 @@ t.test('fastify-oauth2', t => { this.getAccessTokenFromAuthorizationCodeFlow(request, (err, result) => { if (err) throw err - const token = this.githubOAuth2.accessToken.create(result) - reply.send({ - access_token: token.token.access_token, - refresh_token: token.token.refresh_token, - expires_in: token.token.expires_in, - token_type: token.token.token_type + // attempts to refresh the token + this.getNewAccessTokenWithRefreshToken(result.refresh_token, undefined, (err, result) => { + if (err) throw err + + const newToken = result + + reply.send({ + access_token: newToken.token.access_token, + refresh_token: newToken.token.refresh_token, + expires_in: newToken.token.expires_in, + token_type: newToken.token.token_type + }) }) }) }) @@ -109,7 +124,10 @@ t.test('fastify-oauth2', t => { fastify.get('/', function (request, reply) { return this.getAccessTokenFromAuthorizationCodeFlow(request) .then(result => { - const token = this.githubOAuth2.accessToken.create(result) + // attempts to refresh the token + return this.getNewAccessTokenWithRefreshToken(result.refresh_token) + }) + .then(token => { return { access_token: token.token.access_token, refresh_token: token.token.refresh_token, From 4592fc0a26a76e8061dbd07df578c8ecb8aa0260 Mon Sep 17 00:00:00 2001 From: Luciano Date: Sun, 8 Sep 2019 15:21:51 +0100 Subject: [PATCH 2/3] Renamed function to `getNewAccessTokenUsingRefreshToken` and added more documentation in the README --- README.md | 19 +++++++++++++++---- index.js | 12 ++++++------ test.js | 4 ++-- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 4975c2a..bee6ffa 100644 --- a/README.md +++ b/README.md @@ -31,14 +31,14 @@ fastify.register(oauthPlugin, { }) fastify.get('/login/facebook/callback', async function (request, reply) { - const result = await this.getAccessTokenFromAuthorizationCodeFlow(request) + const token = await this.getAccessTokenFromAuthorizationCodeFlow(request) - console.log(result.access_token) + console.log(token.access_token) // if later you need to refresh the token you can use - // this.getNewAccessTokenWithRefreshToken(result.refresh_token) + // const newToken = await this.getNewAccessTokenUsingRefreshToken(token.refresh_token) - reply.send({ access_token: result.access_token }) + reply.send({ access_token: token.access_token }) }) ``` @@ -51,6 +51,17 @@ See [facebook example](./examples/facebook.js) for an example. This fastify plugin decorates the fastify instance with the [`simple-oauth2`](https://github.com/lelylan/simple-oauth2) instance. +## Utilities + +This fastify plugin adds 2 utility decorators to your fastify instance: + + - `getAccessTokenFromAuthorizationCodeFlow(request, callback)`: A function that uses the Authorization code flow to fetch an OAuth2 token using the data in the last request of the flow. If the callback is not passed it will return a promise. The object resulting from the callback call or the promise resolution is a *token response* object containing the following keys: + - `access_token` + - `refresh_token` (optional, only if the `offline scope` was originally requested) + - `token_type` (generally `'bearer'`) + - `expires_in` (number of seconds for the token to expire, e.g. `240000`) + - `getNewAccessTokenUsingRefreshToken(refreshToken, params, callback)`: A function that takes a refresh token and retrieves a new *token response* object. This is generally useful with background processing workers to re-issue a new token when the original token has expired. The `params` argument is optional and it's an object that can be used to pass in extra parameters to the refresh request (e.g. a stricter set of scopes). If the callback is not passed this function will return a promise. The object resulting from the callback call or the promise resolution is a new *token response* object (see fields above). + ## License Licensed under [MIT](./LICENSE). diff --git a/index.js b/index.js index e2a1ab8..e1f9ba4 100644 --- a/index.js +++ b/index.js @@ -94,17 +94,17 @@ const oauthPlugin = fp(function (fastify, options, next) { getAccessTokenFromAuthorizationCodeFlowCallbacked(request, callback) } - function getNewAccessTokenWithRefreshTokenCallbacked (refreshToken, params, callback) { + function getNewAccessTokenUsingRefreshTokenCallbacked (refreshToken, params, callback) { const accessToken = fastify[name].accessToken.create({ refresh_token: refreshToken }) accessToken.refresh(params, callback) } - const getNewAccessTokenWithRefreshTokenPromisified = promisify(getNewAccessTokenWithRefreshTokenCallbacked) + const getNewAccessTokenUsingRefreshTokenPromisified = promisify(getNewAccessTokenUsingRefreshTokenCallbacked) - function getNewAccessTokenWithRefreshToken (refreshToken, params, callback) { + function getNewAccessTokenUsingRefreshToken (refreshToken, params, callback) { if (!callback) { - return getNewAccessTokenWithRefreshTokenPromisified(refreshToken, params) + return getNewAccessTokenUsingRefreshTokenPromisified(refreshToken, params) } - getNewAccessTokenWithRefreshTokenCallbacked(refreshToken, params, callback) + getNewAccessTokenUsingRefreshTokenCallbacked(refreshToken, params, callback) } const oauth2 = oauth2Module.create(credentials) @@ -112,7 +112,7 @@ const oauthPlugin = fp(function (fastify, options, next) { if (startRedirectPath) { fastify.get(startRedirectPath, startRedirectHandler) fastify.decorate('getAccessTokenFromAuthorizationCodeFlow', getAccessTokenFromAuthorizationCodeFlow) - fastify.decorate('getNewAccessTokenWithRefreshToken', getNewAccessTokenWithRefreshToken) + fastify.decorate('getNewAccessTokenUsingRefreshToken', getNewAccessTokenUsingRefreshToken) } try { diff --git a/test.js b/test.js index 3957ad1..d99972d 100644 --- a/test.js +++ b/test.js @@ -84,7 +84,7 @@ t.test('fastify-oauth2', t => { if (err) throw err // attempts to refresh the token - this.getNewAccessTokenWithRefreshToken(result.refresh_token, undefined, (err, result) => { + this.getNewAccessTokenUsingRefreshToken(result.refresh_token, undefined, (err, result) => { if (err) throw err const newToken = result @@ -125,7 +125,7 @@ t.test('fastify-oauth2', t => { return this.getAccessTokenFromAuthorizationCodeFlow(request) .then(result => { // attempts to refresh the token - return this.getNewAccessTokenWithRefreshToken(result.refresh_token) + return this.getNewAccessTokenUsingRefreshToken(result.refresh_token) }) .then(token => { return { From fa0d8cbb14b0f693bcad88ec3868ebed3031a0c3 Mon Sep 17 00:00:00 2001 From: Luciano Mammino Date: Sun, 8 Sep 2019 20:39:15 +0100 Subject: [PATCH 3/3] Update README.md Fixed spacing in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bee6ffa..7cdaaff 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ This fastify plugin adds 2 utility decorators to your fastify instance: - `access_token` - `refresh_token` (optional, only if the `offline scope` was originally requested) - `token_type` (generally `'bearer'`) - - `expires_in` (number of seconds for the token to expire, e.g. `240000`) + - `expires_in` (number of seconds for the token to expire, e.g. `240000`) - `getNewAccessTokenUsingRefreshToken(refreshToken, params, callback)`: A function that takes a refresh token and retrieves a new *token response* object. This is generally useful with background processing workers to re-issue a new token when the original token has expired. The `params` argument is optional and it's an object that can be used to pass in extra parameters to the refresh request (e.g. a stricter set of scopes). If the callback is not passed this function will return a promise. The object resulting from the callback call or the promise resolution is a new *token response* object (see fields above). ## License