Skip to content

Commit af550d4

Browse files
atinuxPooya Parsa
authored andcommitted
feat(oauth2): support server-side callback (#381)
1 parent 79e7318 commit af550d4

File tree

8 files changed

+50
-37
lines changed

8 files changed

+50
-37
lines changed

docs/api/auth.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[Source Code](https://github.com/nuxt-community/auth-module/blob/dev/lib/core/auth.js)
44

55
This module globally injects `$auth` instance, meaning that you can access it anywhere using `this.$auth`.
6-
For plugins, asyncData, fetch, nuxtServerInit and Middleware, you can access it from `context.app.$auth`.
6+
For plugins, asyncData, fetch, nuxtServerInit and Middleware, you can access it from `context.$auth`.
77

88
## properties
99

@@ -118,8 +118,8 @@ this.$auth.setToken('local', '.....')
118118
Listen for auth errors: (`plugins/auth.js`)
119119

120120
```js
121-
export default function({ app }) {
122-
app.$auth.onError((error, name, endpoint) => {
121+
export default function({ $auth }) {
122+
$auth.onError((error, name, endpoint) => {
123123
console.error(name, error)
124124
})
125125
}
@@ -130,8 +130,8 @@ export default function({ app }) {
130130
Pre-process URLs before redirect: (`plugins/auth.js`)
131131

132132
```js
133-
export default function({ app }) {
134-
app.$auth.onRedirect((to, from) => {
133+
export default function({ $auth }) {
134+
$auth.onRedirect((to, from) => {
135135
console.error(to)
136136
// you can optionally change `to` by returning a new value
137137
})

docs/recipes/extend.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ If you have plugins that need to access `$auth`, you can use `auth.plugins` opti
1818
`plugins/auth.js`
1919

2020
```js
21-
export default function ({ app }) {
22-
if (!app.$auth.loggedIn) {
21+
export default function ({ $auth }) {
22+
if (!$auth.loggedIn) {
2323
return
2424
}
2525

26-
const username = app.$auth.user.username
26+
const username = $auth.user.username
2727
}
2828
```

lib/core/middleware.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,22 @@ Middleware.auth = function (ctx) {
1414
return
1515
}
1616

17-
const { login, callback } = ctx.app.$auth.options.redirect
17+
const { login, callback } = ctx.$auth.options.redirect
1818
const pageIsInGuestMode = routeOption(ctx.route, 'auth', 'guest')
1919
const insideLoginPage = normalizePath(ctx.route.path) === normalizePath(login)
2020
const insideCallbackPage = normalizePath(ctx.route.path) !== normalizePath(callback)
2121

22-
if (ctx.app.$auth.$state.loggedIn) {
22+
if (ctx.$auth.$state.loggedIn) {
2323
// -- Authorized --
2424
if (!login || insideLoginPage || pageIsInGuestMode) {
25-
ctx.app.$auth.redirect('home')
25+
ctx.$auth.redirect('home')
2626
}
2727
} else {
2828
// -- Guest --
2929
// (Those passing `callback` at runtime need to mark their callback component
3030
// with `auth: false` to avoid an unnecessary redirect from callback to login)
3131
if (!pageIsInGuestMode && (!callback || insideCallbackPage)) {
32-
ctx.app.$auth.redirect('login')
32+
ctx.$auth.redirect('login')
3333
}
3434
}
3535
}

lib/module/plugin.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,7 @@ export default function (ctx, inject) {
1212
// Create a new Auth instance
1313
const $auth = new Auth(ctx, options)
1414

15-
// Inject it to nuxt context as $auth
16-
inject('auth', $auth)
17-
1815
// Register strategies
19-
2016
<%=
2117
options.strategies.map(strategy => {
2218
const scheme = 'scheme_' + hash(options.strategyScheme.get(strategy))
@@ -26,6 +22,10 @@ export default function (ctx, inject) {
2622
}).join('\n\n ')
2723
%>
2824

25+
// Inject it to nuxt context as $auth
26+
inject('auth', $auth)
27+
ctx.$auth = $auth
28+
2929
// Initialize auth
3030
return $auth.init().catch(error => {
3131
if (process.client) {

lib/schemes/oauth2.js

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { encodeQuery, parseQuery } from '../utilities'
1+
import { encodeQuery } from '../utilities'
22
import nanoid from 'nanoid'
3+
const isHttps = process.server ? require('is-https') : null
34

45
const DEFAULTS = {
56
token_type: 'Bearer',
@@ -10,6 +11,7 @@ const DEFAULTS = {
1011
export default class Oauth2Scheme {
1112
constructor (auth, options) {
1213
this.$auth = auth
14+
this.req = auth.ctx.req
1315
this.name = options._name
1416

1517
this.options = Object.assign({}, DEFAULTS, options)
@@ -28,6 +30,12 @@ export default class Oauth2Scheme {
2830
return url
2931
}
3032

33+
if (process.server && this.req) {
34+
const protocol = 'http' + (isHttps(this.req) ? 's' : '') + '://'
35+
36+
return protocol + this.req.headers.host + this.$auth.options.redirect.callback
37+
}
38+
3139
if (process.client) {
3240
return window.location.origin + this.$auth.options.redirect.callback
3341
}
@@ -91,7 +99,7 @@ export default class Oauth2Scheme {
9199
opts.nonce = nonce || nanoid()
92100
}
93101

94-
this.$auth.$storage.setLocalStorage(this.name + '.state', opts.state)
102+
this.$auth.$storage.setUniversal(this.name + '.state', opts.state)
95103

96104
const url = this.options.authorization_endpoint + '?' + encodeQuery(opts)
97105

@@ -116,28 +124,34 @@ export default class Oauth2Scheme {
116124
}
117125

118126
async _handleCallback (uri) {
119-
// Callback flow is not supported in server side
120-
if (process.server) {
127+
// Handle callback only for specified route
128+
if (this.$auth.options.redirect && this.$auth.ctx.route.path !== this.$auth.options.redirect.callback) {
129+
return
130+
}
131+
// Callback flow is not supported in static generation
132+
if (process.server && process.static) {
121133
return
122134
}
123135

124-
// Parse query from both search and hash fragments
125-
const hash = parseQuery(window.location.hash.substr(1))
126-
const search = parseQuery(window.location.search.substr(1))
127-
const parsedQuery = Object.assign({}, search, hash)
128-
136+
const parsedQuery = Object.assign({}, this.$auth.ctx.route.query, this.$auth.ctx.route.hash)
129137
// accessToken/idToken
130138
let token = parsedQuery[this.options.token_key || 'access_token']
131-
132139
// refresh token
133140
let refreshToken = parsedQuery[this.options.refresh_token_key || 'refresh_token']
134141

142+
// Validate state
143+
const state = this.$auth.$storage.getUniversal(this.name + '.state')
144+
this.$auth.$storage.setUniversal(this.name + '.state', null)
145+
if (state && parsedQuery.state !== state) {
146+
return
147+
}
148+
135149
// -- Authorization Code Grant --
136150
if (this.options.response_type === 'code' && parsedQuery.code) {
137-
const data = await this.$auth.request({
151+
let data = await this.$auth.request({
138152
method: 'post',
139153
url: this.options.access_token_endpoint,
140-
baseURL: false,
154+
baseURL: process.server ? undefined : false,
141155
data: encodeQuery({
142156
code: parsedQuery.code,
143157
client_id: this.options.client_id,
@@ -161,13 +175,6 @@ export default class Oauth2Scheme {
161175
return
162176
}
163177

164-
// Validate state
165-
const state = this.$auth.$storage.getLocalStorage(this.name + '.state')
166-
this.$auth.$storage.setLocalStorage(this.name + '.state', null)
167-
if (state && parsedQuery.state !== state) {
168-
return
169-
}
170-
171178
// Append token_type
172179
if (this.options.token_type) {
173180
token = this.options.token_type + ' ' + token

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"consola": "^2.9.0",
4646
"cookie": "^0.4.0",
4747
"dotprop": "^1.2.0",
48+
"is-https": "^1.0.0",
4849
"js-cookie": "^2.2.0",
4950
"lodash": "^4.17.11",
5051
"nanoid": "^2.0.3"

test/fixtures/basic/plugins/auth.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
export default function ({ app }) {
2-
app.$auth._custom_plugin = true
1+
export default function ({ $auth }) {
2+
$auth._custom_plugin = true
33
}

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5219,6 +5219,11 @@ is-glob@^4.0.0, is-glob@^4.0.1:
52195219
dependencies:
52205220
is-extglob "^2.1.1"
52215221

5222+
is-https@^1.0.0:
5223+
version "1.0.0"
5224+
resolved "https://registry.yarnpkg.com/is-https/-/is-https-1.0.0.tgz#9c1dde000dc7e7288edb983bef379e498e7cb1bf"
5225+
integrity sha512-1adLLwZT9XEXjzhQhZxd75uxf0l+xI9uTSFaZeSESjL3E1eXSPpO+u5RcgqtzeZ1KCaNvtEwZSTO2P4U5erVqQ==
5226+
52225227
is-number@^3.0.0:
52235228
version "3.0.0"
52245229
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"

0 commit comments

Comments
 (0)