Skip to content
Permalink
Browse files
feat(oauth2): support server-side callback (#381)
  • Loading branch information
Atinux authored and Pooya Parsa committed Jun 23, 2019
1 parent 79e7318 commit af550d4d63fa02d167089b0826ffca78321baf13
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 37 deletions.
@@ -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
})
@@ -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
}
```
@@ -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')
}
}
}
@@ -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) {
@@ -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
@@ -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"
@@ -1,3 +1,3 @@
export default function ({ app }) {
app.$auth._custom_plugin = true
export default function ({ $auth }) {
$auth._custom_plugin = true
}
@@ -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"

0 comments on commit af550d4

Please sign in to comment.