Skip to content

Commit 9c1ff08

Browse files
feat: expose generateAuthrizationUri to decorator (#46)
1 parent b164aa1 commit 9c1ff08

File tree

3 files changed

+64
-8
lines changed

3 files changed

+64
-8
lines changed

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,16 +147,24 @@ Assuming we have registered multiple OAuth providers like this:
147147

148148
## Utilities
149149

150-
This fastify plugin adds 2 utility decorators to your fastify instance using the same **namespace**:
150+
This fastify plugin adds 3 utility decorators to your fastify instance using the same **namespace**:
151151

152152
- `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:
153153
- `access_token`
154154
- `refresh_token` (optional, only if the `offline scope` was originally requested)
155155
- `token_type` (generally `'bearer'`)
156156
- `expires_in` (number of seconds for the token to expire, e.g. `240000`)
157157
- `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).
158+
- `generateAuthorizationUri(requestObject)`: A function that returns the authorization uri. This is generally useful when you want to handle the redirect yourself in a specific route. The `requestObject` argument passes the request object to the `generateStateFunction`). You **don't** need to declare a `startRedirectPath` if you use this approach. Example of how you would use it:
158159

159-
E.g. For `name: 'customOauth2'`, both helpers `getAccessTokenFromAuthorizationCodeFlow` and `getNewAccessTokenUsingRefreshToken` will become accessible like this:
160+
```js
161+
fastify.get('/external', { /* Hooks can be used here */ }, async (req, reply) => {
162+
const authorizationEndpoint = fastify.customOAuth2.generateAuthorizationUri(req);
163+
reply.redirect(authorizationEndpoint)
164+
});
165+
```
166+
167+
E.g. For `name: 'customOauth2'`, the helpers `getAccessTokenFromAuthorizationCodeFlow` and `getNewAccessTokenUsingRefreshToken` will become accessible like this:
160168

161169
- `fastify.customOauth2.getAccessTokenFromAuthorizationCodeFlow`
162170
- `fastify.customOauth2.getNewAccessTokenUsingRefreshToken`

index.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,21 @@ const oauthPlugin = fp(function (fastify, options, next) {
5454
const checkStateFunction = options.checkStateFunction || defaultCheckStateFunction
5555
const startRedirectPath = options.startRedirectPath
5656

57-
function startRedirectHandler (request, reply) {
58-
const state = generateStateFunction(request)
57+
function generateAuthorizationUri (requestObject) {
58+
const state = generateStateFunction(requestObject)
5959
const urlOptions = Object.assign({}, callbackUriParams, {
6060
redirect_uri: callbackUri,
6161
scope: scope,
6262
state: state
6363
})
6464

65-
const authorizationUri = this[name].oauth2.authorizationCode.authorizeURL(urlOptions)
65+
const authorizationUri = oauth2.authorizationCode.authorizeURL(urlOptions)
66+
return authorizationUri
67+
}
68+
69+
function startRedirectHandler (request, reply) {
70+
const authorizationUri = generateAuthorizationUri(request)
71+
6672
reply.redirect(authorizationUri)
6773
}
6874

@@ -117,7 +123,8 @@ const oauthPlugin = fp(function (fastify, options, next) {
117123
fastify.decorate(name, {
118124
oauth2: oauth2,
119125
getAccessTokenFromAuthorizationCodeFlow,
120-
getNewAccessTokenUsingRefreshToken
126+
getNewAccessTokenUsingRefreshToken,
127+
generateAuthorizationUri
121128
})
122129
} catch (e) {
123130
next(e)

test.js

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,47 @@ t.test('options.generateStateFunction with request', t => {
330330
})
331331
})
332332

333+
t.test('generateAuthorizationUri redirect with request object', t => {
334+
t.plan(4)
335+
const fastify = createFastify()
336+
337+
fastify.register(oauthPlugin, {
338+
name: 'theName',
339+
credentials: {
340+
client: {
341+
id: 'my-client-id',
342+
secret: 'my-secret'
343+
},
344+
auth: oauthPlugin.GITHUB_CONFIGURATION
345+
},
346+
callbackUri: '/callback',
347+
generateStateFunction: (request) => {
348+
t.ok(request, 'the request param has been set')
349+
return request.query.code
350+
},
351+
checkStateFunction: () => true,
352+
scope: ['notifications']
353+
})
354+
355+
fastify.get('/gh', function (request, reply) {
356+
const redirectUrl = this.theName.generateAuthorizationUri(request)
357+
return reply.redirect(redirectUrl)
358+
})
359+
360+
t.tearDown(fastify.close.bind(fastify))
361+
362+
fastify.inject({
363+
method: 'GET',
364+
url: '/gh',
365+
query: { code: 'generated_code' }
366+
}, function (err, responseStart) {
367+
t.error(err)
368+
t.equal(responseStart.statusCode, 302)
369+
const matched = responseStart.headers.location.match(/https:\/\/github\.com\/login\/oauth\/authorize\?response_type=code&client_id=my-client-id&redirect_uri=%2Fcallback&scope=notifications&state=generated_code/)
370+
t.ok(matched)
371+
})
372+
})
373+
333374
t.test('options.generateStateFunction should be an object', t => {
334375
t.plan(1)
335376

@@ -367,7 +408,7 @@ t.test('options.checkStateFunction should be an object', t => {
367408
auth: oauthPlugin.GITHUB_CONFIGURATION
368409
},
369410
callbackUri: '/callback',
370-
generateStateFunction: () => {},
411+
generateStateFunction: () => { },
371412
checkStateFunction: 42
372413
})
373414
.ready(err => {
@@ -412,7 +453,7 @@ t.test('options.generateStateFunction ^ options.checkStateFunction', t => {
412453
auth: oauthPlugin.GITHUB_CONFIGURATION
413454
},
414455
callbackUri: '/callback',
415-
checkStateFunction: () => {}
456+
checkStateFunction: () => { }
416457
})
417458
.ready(err => {
418459
t.strictSame(err.message, 'options.checkStateFunction and options.generateStateFunction have to be given')

0 commit comments

Comments
 (0)