Skip to content

feat(oauth2): Add support for runtime and environment-specific options #460

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions docs/schemes/oauth2.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,32 @@ By default is set to `refresh_token_key: 'refresh_token'`. It automatically stor
By default is set to random generated string.

The primary reason for using the state parameter is to mitigate CSRF attacks. ([read more](https://auth0.com/docs/protocols/oauth2/oauth-state))

### `_runtimeOptions`

`NOTE: Only available in univeral mode`


By default all options are baked into the bundle during build.
If you use the same bundle in multiple environments (e.g. test and prod), but have options that are environment-specific,
you can supply a function like this:

```js
auth: {
strategies: {
yourFavoriteProvider: {
_scheme: 'oauth2',
scope: ['openid', 'profile', 'email'],
_runtimeOptions: () => {
return {
client_id: process.env.CLIENT_ID,
authorization_endpoint: process.env.AUTH_URL + '/auth',
access_token_endpoint: process.env.AUTH_URL + '/token',
userinfo_endpoint: process.env.AUTH_URL + '/userinfo'
}
}
}
}
}
```
The `_runtimeOptions` function will be evaluated and merged with the other options during the initial request to the server.
35 changes: 35 additions & 0 deletions lib/core/utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ export const isSameURL = (a, b) => a.split('?')[0] === b.split('?')[0]
export const isRelativeURL = u =>
u && u.length && /^\/[a-zA-Z0-9@\-%_~][/a-zA-Z0-9@\-%_~]*[?]?([^#]*)#?([^#]*)$/.test(u)

const isSpaMode = ctx => process.client && !ctx.nuxtState

// eslint-disable-next-line
const functionFromString = obj => Function('"use strict";return (' + obj + ')')()

export const parseQuery = queryString => {
const query = {}
const pairs = queryString.split('&')
Expand Down Expand Up @@ -83,3 +88,33 @@ export function decodeValue (val) {
// Return as is
return val
}

export function handleRuntimeOptions (ctx, options) {
// _runtimeOptions is only supported in universal mode
if (isSpaMode(ctx)) {
if (options._runtimeOptions) {
// eslint-disable-next-line
console.error('[ERROR] [AUTH]: _runtimeOptions is only supported in universal mode.')
}
return {}
}

const key = 'auth._runtimeOptions.' + options._name

// Evaluate and share _runtimeOptions with client
if (process.server && options._runtimeOptions) {
const runtimeOptions = functionFromString(options._runtimeOptions)()

ctx.beforeNuxtRender(({ nuxtState }) => {
nuxtState[key] = runtimeOptions
})
return runtimeOptions
}

// Fetch shared _runtimeOptions from client side
if (process.client && ctx.nuxtState && ctx.nuxtState[key]) {
return ctx.nuxtState[key]
}

return {}
}
10 changes: 9 additions & 1 deletion lib/module/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,16 @@ export default function (ctx, inject) {
// Register strategies
<%=
options.strategies.map(strategy => {
function getSchemeOptions(schemeOptions) {
// _runtimeOptions function must be stringified (not supported by JSON.stringify)
if (schemeOptions._runtimeOptions) {
schemeOptions._runtimeOptions = schemeOptions._runtimeOptions.toString()
}
return JSON.stringify(schemeOptions)
}

const scheme = 'scheme_' + hash(options.strategyScheme.get(strategy))
const schemeOptions = JSON.stringify(strategy)
const schemeOptions = getSchemeOptions(strategy)
const name = strategy._name
return `// ${name}\n $auth.registerStrategy('${name}', new ${scheme}($auth, ${schemeOptions}))`
}).join('\n\n ')
Expand Down
6 changes: 4 additions & 2 deletions lib/schemes/oauth2.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { encodeQuery, parseQuery } from '../utilities'
import { encodeQuery, parseQuery, handleRuntimeOptions } from '../utilities'
import nanoid from 'nanoid'
const isHttps = process.server ? require('is-https') : null

Expand All @@ -14,7 +14,9 @@ export default class Oauth2Scheme {
this.req = auth.ctx.req
this.name = options._name

this.options = Object.assign({}, DEFAULTS, options)
const runtimeOptions = handleRuntimeOptions(auth.ctx, options)

this.options = Object.assign({}, DEFAULTS, options, runtimeOptions)
}

get _scope () {
Expand Down