diff --git a/docs/api/auth.md b/docs/api/auth.md index fd5998a20..591eb1f32 100644 --- a/docs/api/auth.md +++ b/docs/api/auth.md @@ -3,7 +3,7 @@ [Source Code](https://github.com/nuxt-community/auth-module/blob/dev/lib/core/auth.js) This module globally injects `$auth` instance, meaning that you can access it anywhere using `this.$auth`. -For plugins, asyncData, fetch, nuxtServerInit and Middleware, you can access it from `context.app.$auth`. +For plugins, asyncData, fetch, nuxtServerInit and Middleware, you can access it from `context.$auth`. ## properties @@ -118,8 +118,8 @@ this.$auth.setToken('local', '.....') Listen for auth errors: (`plugins/auth.js`) ```js -export default function({ app }) { - app.$auth.onError((error, name, endpoint) => { +export default function({ $auth }) { + $auth.onError((error, name, endpoint) => { console.error(name, error) }) } @@ -130,8 +130,8 @@ export default function({ app }) { Pre-process URLs before redirect: (`plugins/auth.js`) ```js -export default function({ app }) { - app.$auth.onRedirect((to, from) => { +export default function({ $auth }) { + $auth.onRedirect((to, from) => { console.error(to) // you can optionally change `to` by returning a new value }) diff --git a/docs/recipes/extend.md b/docs/recipes/extend.md index 19b529f7f..7f27d453a 100644 --- a/docs/recipes/extend.md +++ b/docs/recipes/extend.md @@ -18,11 +18,11 @@ If you have plugins that need to access `$auth`, you can use `auth.plugins` opti `plugins/auth.js` ```js -export default function ({ app }) { - if (!app.$auth.loggedIn) { +export default function ({ $auth }) { + if (!$auth.loggedIn) { return } - const username = app.$auth.user.username + const username = $auth.user.username } ``` diff --git a/lib/core/middleware.js b/lib/core/middleware.js index 5002090b2..ba89a9a96 100644 --- a/lib/core/middleware.js +++ b/lib/core/middleware.js @@ -14,22 +14,22 @@ Middleware.auth = function (ctx) { return } - const { login, callback } = ctx.app.$auth.options.redirect + const { login, callback } = ctx.$auth.options.redirect const pageIsInGuestMode = routeOption(ctx.route, 'auth', 'guest') const insideLoginPage = normalizePath(ctx.route.path) === normalizePath(login) const insideCallbackPage = normalizePath(ctx.route.path) !== normalizePath(callback) - if (ctx.app.$auth.$state.loggedIn) { + if (ctx.$auth.$state.loggedIn) { // -- Authorized -- if (!login || insideLoginPage || pageIsInGuestMode) { - ctx.app.$auth.redirect('home') + ctx.$auth.redirect('home') } } else { // -- Guest -- // (Those passing `callback` at runtime need to mark their callback component // with `auth: false` to avoid an unnecessary redirect from callback to login) if (!pageIsInGuestMode && (!callback || insideCallbackPage)) { - ctx.app.$auth.redirect('login') + ctx.$auth.redirect('login') } } } diff --git a/lib/module/plugin.js b/lib/module/plugin.js index 4f7dd80b9..e1c4bdff5 100644 --- a/lib/module/plugin.js +++ b/lib/module/plugin.js @@ -12,11 +12,7 @@ export default function (ctx, inject) { // Create a new Auth instance const $auth = new Auth(ctx, options) - // Inject it to nuxt context as $auth - inject('auth', $auth) - // Register strategies - <%= options.strategies.map(strategy => { const scheme = 'scheme_' + hash(options.strategyScheme.get(strategy)) @@ -26,6 +22,10 @@ export default function (ctx, inject) { }).join('\n\n ') %> + // Inject it to nuxt context as $auth + inject('auth', $auth) + ctx.$auth = $auth + // Initialize auth return $auth.init().catch(error => { if (process.client) { diff --git a/lib/schemes/oauth2.js b/lib/schemes/oauth2.js index 387fc2cbd..389c5d0e0 100644 --- a/lib/schemes/oauth2.js +++ b/lib/schemes/oauth2.js @@ -1,5 +1,6 @@ -import { encodeQuery, parseQuery } from '../utilities' +import { encodeQuery } from '../utilities' import nanoid from 'nanoid' +const isHttps = process.server ? require('is-https') : null const DEFAULTS = { token_type: 'Bearer', @@ -10,6 +11,7 @@ const DEFAULTS = { export default class Oauth2Scheme { constructor (auth, options) { this.$auth = auth + this.req = auth.ctx.req this.name = options._name this.options = Object.assign({}, DEFAULTS, options) @@ -28,6 +30,12 @@ export default class Oauth2Scheme { return url } + if (process.server && this.req) { + const protocol = 'http' + (isHttps(this.req) ? 's' : '') + '://' + + return protocol + this.req.headers.host + this.$auth.options.redirect.callback + } + if (process.client) { return window.location.origin + this.$auth.options.redirect.callback } @@ -91,7 +99,7 @@ export default class Oauth2Scheme { opts.nonce = nonce || nanoid() } - this.$auth.$storage.setLocalStorage(this.name + '.state', opts.state) + this.$auth.$storage.setUniversal(this.name + '.state', opts.state) const url = this.options.authorization_endpoint + '?' + encodeQuery(opts) @@ -116,28 +124,34 @@ export default class Oauth2Scheme { } async _handleCallback (uri) { - // Callback flow is not supported in server side - if (process.server) { + // Handle callback only for specified route + if (this.$auth.options.redirect && this.$auth.ctx.route.path !== this.$auth.options.redirect.callback) { + return + } + // Callback flow is not supported in static generation + if (process.server && process.static) { return } - // Parse query from both search and hash fragments - const hash = parseQuery(window.location.hash.substr(1)) - const search = parseQuery(window.location.search.substr(1)) - const parsedQuery = Object.assign({}, search, hash) - + const parsedQuery = Object.assign({}, this.$auth.ctx.route.query, this.$auth.ctx.route.hash) // accessToken/idToken let token = parsedQuery[this.options.token_key || 'access_token'] - // refresh token let refreshToken = parsedQuery[this.options.refresh_token_key || 'refresh_token'] + // Validate state + const state = this.$auth.$storage.getUniversal(this.name + '.state') + this.$auth.$storage.setUniversal(this.name + '.state', null) + if (state && parsedQuery.state !== state) { + return + } + // -- Authorization Code Grant -- if (this.options.response_type === 'code' && parsedQuery.code) { - const data = await this.$auth.request({ + let data = await this.$auth.request({ method: 'post', url: this.options.access_token_endpoint, - baseURL: false, + baseURL: process.server ? undefined : false, data: encodeQuery({ code: parsedQuery.code, client_id: this.options.client_id, @@ -161,13 +175,6 @@ export default class Oauth2Scheme { return } - // Validate state - const state = this.$auth.$storage.getLocalStorage(this.name + '.state') - this.$auth.$storage.setLocalStorage(this.name + '.state', null) - if (state && parsedQuery.state !== state) { - return - } - // Append token_type if (this.options.token_type) { token = this.options.token_type + ' ' + token diff --git a/package.json b/package.json index d2ed16cff..7c2daeb4b 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "consola": "^2.9.0", "cookie": "^0.4.0", "dotprop": "^1.2.0", + "is-https": "^1.0.0", "js-cookie": "^2.2.0", "lodash": "^4.17.11", "nanoid": "^2.0.3" diff --git a/test/fixtures/basic/plugins/auth.js b/test/fixtures/basic/plugins/auth.js index 6cdfbeac6..5887464f4 100644 --- a/test/fixtures/basic/plugins/auth.js +++ b/test/fixtures/basic/plugins/auth.js @@ -1,3 +1,3 @@ -export default function ({ app }) { - app.$auth._custom_plugin = true +export default function ({ $auth }) { + $auth._custom_plugin = true } diff --git a/yarn.lock b/yarn.lock index b47a16ccb..2e5f12920 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5219,6 +5219,11 @@ is-glob@^4.0.0, is-glob@^4.0.1: dependencies: is-extglob "^2.1.1" +is-https@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-https/-/is-https-1.0.0.tgz#9c1dde000dc7e7288edb983bef379e498e7cb1bf" + integrity sha512-1adLLwZT9XEXjzhQhZxd75uxf0l+xI9uTSFaZeSESjL3E1eXSPpO+u5RcgqtzeZ1KCaNvtEwZSTO2P4U5erVqQ== + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"