Skip to content

Commit 73fd1fe

Browse files
feat: improve token handling and validation (#690)
Initialize Token and Refresh Token instances inside schemes. Both classes now use scheme options instead of `$auth`. Also move token and refresh token prefixes to scheme `DEFAULTS`. Improve `check` method to also check tokens status, deprecating `_checkStatus`. Return object with token and refresh token expired status. ```js { valid: boolean, tokenExpired: boolean, refreshTokenExpired: boolean, isRefreshable: boolean } ``` **Note:** `valid` is the only required property. Alias `HTTPRequest` to `AxiosRequestConfig` and `HTTPResponse` to `AxiosResponse`. RequestHandler class now use scheme options and methods instead of `$auth`.
1 parent 6e1461a commit 73fd1fe

File tree

14 files changed

+373
-275
lines changed

14 files changed

+373
-275
lines changed

demo/pages/secure.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
Test: <b-badge>{{ $auth.hasScope('test') }}</b-badge>
1616
Admin: <b-badge>{{ $auth.hasScope('admin') }}</b-badge>
1717
</b-card>
18-
<b-card title="token" class="mb-2">
18+
<b-card v-if="$auth.strategy.token" title="token" class="mb-2">
1919
<div style="white-space: nowrap; overflow: auto">
20-
{{ $auth.token.get() || '-' }}
20+
{{ $auth.strategy.token.get() || '-' }}
2121
</div>
2222
</b-card>
23-
<b-card title="refresh token">
23+
<b-card v-if="$auth.strategy.refreshToken" title="refresh token">
2424
<div style="white-space: nowrap; overflow: auto">
25-
{{ $auth.refreshToken.get() || '-' }}
25+
{{ $auth.strategy.refreshToken.get() || '-' }}
2626
</div>
2727
</b-card>
2828
</b-col>

src/core/auth.ts

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import { routeOption, isRelativeURL, isSet, isSameURL, getProp } from '../utils'
2-
import RefreshToken from '../inc/refresh-token'
3-
import Token from '../inc/token'
42
import type { AuthOptions, HTTPRequest, HTTPResponse } from '../'
53
import Storage from './storage'
64

@@ -10,9 +8,6 @@ export default class Auth {
108
public strategies = {}
119
public error: Error
1210

13-
public token: Token
14-
public refreshToken: RefreshToken
15-
1611
private _errorListeners = []
1712
private _redirectListeners = []
1813
private _stateWarnShown: boolean
@@ -30,10 +25,6 @@ export default class Auth {
3025
const storage = new Storage(ctx, options)
3126
this.$storage = storage
3227
this.$state = storage.state
33-
34-
// Token & Refresh Token
35-
this.token = new Token(this)
36-
this.refreshToken = new RefreshToken(this)
3728
}
3829

3930
async init () {
@@ -180,7 +171,7 @@ export default class Auth {
180171

181172
setUserToken (token) {
182173
if (!this.strategy.setUserToken) {
183-
this.token.set(token)
174+
this.strategy.token.set(token)
184175
return Promise.resolve()
185176
}
186177

@@ -193,8 +184,8 @@ export default class Auth {
193184
reset (...args) {
194185
if (!this.strategy.reset) {
195186
this.setUser(false)
196-
this.token.reset()
197-
this.refreshToken.reset()
187+
this.strategy.token.reset()
188+
this.strategy.refreshToken.reset()
198189
}
199190

200191
return this.strategy.reset(...args)
@@ -223,15 +214,12 @@ export default class Auth {
223214
return this.$state.loggedIn
224215
}
225216

226-
check () {
227-
let loggedIn = Boolean(this.$state.user)
228-
229-
if (loggedIn && typeof this.strategy.check === 'function') {
230-
loggedIn = this.strategy.check()
217+
check (...args) {
218+
if (!this.strategy.check) {
219+
return { valid: true }
231220
}
232221

233-
this.$storage.setState('loggedIn', loggedIn)
234-
return loggedIn
222+
return this.strategy.check(...args)
235223
}
236224

237225
fetchUserOnce (...args) {
@@ -243,7 +231,16 @@ export default class Auth {
243231

244232
setUser (user) {
245233
this.$storage.setState('user', user)
246-
this.check()
234+
235+
let check = { valid: Boolean(user) }
236+
237+
// If user is defined, perform scheme checks.
238+
if (check.valid) {
239+
check = this.check()
240+
}
241+
242+
// Update `loggedIn` state
243+
this.$storage.setState('loggedIn', check.valid)
247244
}
248245

249246
// ---------------------------------------------------------------
@@ -254,7 +251,7 @@ export default class Auth {
254251
return this.$storage.getState('busy')
255252
}
256253

257-
request (endpoint: HTTPRequest, defaults = {}): Promise<HTTPResponse> {
254+
request (endpoint: HTTPRequest, defaults: HTTPRequest = {}): Promise<HTTPResponse> {
258255
const _endpoint =
259256
typeof defaults === 'object'
260257
? Object.assign({}, defaults, endpoint)
@@ -278,7 +275,7 @@ export default class Auth {
278275
}
279276

280277
requestWith (strategy: string, endpoint: HTTPRequest, defaults?: HTTPRequest): Promise<HTTPResponse> {
281-
const token = this.token.get()
278+
const token = this.strategy.token.get()
282279

283280
const _endpoint = Object.assign({}, defaults, endpoint)
284281

src/core/middleware.ts

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,27 +23,20 @@ export default async function authMiddleware (ctx) {
2323
ctx.$auth.redirect('home')
2424
}
2525

26-
if (ctx.$auth.strategy &&
27-
ctx.$auth.strategy.options.token &&
28-
ctx.$auth.strategy.options.token.required) {
29-
// Sync tokens
30-
ctx.$auth.token.sync()
31-
const refreshToken = ctx.$auth.refreshToken.sync()
32-
const tokenStatus = ctx.$auth.token.status()
33-
const refreshTokenStatus = ctx.$auth.refreshToken.status()
26+
// Perform scheme checks.
27+
const { tokenExpired, refreshTokenExpired, isRefreshable } = ctx.$auth.check(true)
3428

35-
// Refresh token has expired. There is no way to refresh. Force reset.
36-
if (refreshTokenStatus.expired()) {
37-
await ctx.$auth.reset()
38-
} else if (tokenStatus.expired()) {
39-
// Token has expired.
40-
if (refreshToken) {
41-
// Refresh token is available. Attempt refresh.
42-
await ctx.$auth.refreshTokens()
43-
} else {
44-
// Refresh token is not available. Force reset.
45-
await ctx.$auth.reset()
46-
}
29+
// Refresh token has expired. There is no way to refresh. Force reset.
30+
if (refreshTokenExpired) {
31+
ctx.$auth.reset()
32+
} else if (tokenExpired) {
33+
// Token has expired. Check if refresh token is available.
34+
if (isRefreshable) {
35+
// Refresh token is available. Attempt refresh.
36+
await ctx.$auth.refreshTokens()
37+
} else {
38+
// Refresh token is not available. Force reset.
39+
ctx.$auth.reset()
4740
}
4841
}
4942

src/inc/refresh-token.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,40 @@
11
import jwtDecode, { InvalidTokenError } from 'jwt-decode'
22
import { addTokenPrefix } from '../utils'
3-
import type { Auth } from '../'
3+
import type { Scheme } from '../'
4+
import Storage from '../core/storage'
45
import TokenStatus from './token-status'
56

67
export default class RefreshToken {
7-
public $auth: Auth
8+
public scheme: Scheme
9+
public $storage: Storage
810

9-
constructor (auth: Auth) {
10-
this.$auth = auth
11+
constructor (scheme: Scheme, storage: Storage) {
12+
this.scheme = scheme
13+
this.$storage = storage
1114
}
1215

1316
_getExpiration () {
14-
const _key = this.$auth.options.refreshTokenExpiration.prefix + this.$auth.strategy.name
17+
const _key = this.scheme.options.refreshToken.expirationPrefix + this.scheme.name
1518

16-
return this.$auth.$storage.getUniversal(_key)
19+
return this.$storage.getUniversal(_key)
1720
}
1821

1922
_setExpiration (expiration) {
20-
const _key = this.$auth.options.refreshTokenExpiration.prefix + this.$auth.strategy.name
23+
const _key = this.scheme.options.refreshToken.expirationPrefix + this.scheme.name
2124

22-
return this.$auth.$storage.setUniversal(_key, expiration)
25+
return this.$storage.setUniversal(_key, expiration)
2326
}
2427

2528
_syncExpiration () {
26-
const _key = this.$auth.options.refreshTokenExpiration.prefix + this.$auth.strategy.name
29+
const _key = this.scheme.options.refreshToken.expirationPrefix + this.scheme.name
2730

28-
return this.$auth.$storage.syncUniversal(_key)
31+
return this.$storage.syncUniversal(_key)
2932
}
3033

3134
_updateExpiration (refreshToken) {
3235
let refreshTokenExpiration
3336
const _tokenIssuedAtMillis = Date.now()
34-
const _tokenTTLMillis = this.$auth.strategy.options.refreshToken.maxAge * 1000
37+
const _tokenTTLMillis = this.scheme.options.refreshToken.maxAge * 1000
3538
const _tokenExpiresAtMillis = _tokenTTLMillis ? _tokenIssuedAtMillis + _tokenTTLMillis : 0
3639

3740
try {
@@ -50,25 +53,25 @@ export default class RefreshToken {
5053
}
5154

5255
_setToken (refreshToken) {
53-
const _key = this.$auth.options.refreshToken.prefix + this.$auth.strategy.name
56+
const _key = this.scheme.options.refreshToken.prefix + this.scheme.name
5457

55-
return this.$auth.$storage.setUniversal(_key, refreshToken)
58+
return this.$storage.setUniversal(_key, refreshToken)
5659
}
5760

5861
_syncToken () {
59-
const _key = this.$auth.options.refreshToken.prefix + this.$auth.strategy.name
62+
const _key = this.scheme.options.refreshToken.prefix + this.scheme.name
6063

61-
return this.$auth.$storage.syncUniversal(_key)
64+
return this.$storage.syncUniversal(_key)
6265
}
6366

6467
get () {
65-
const _key = this.$auth.options.refreshToken.prefix + this.$auth.strategy.name
68+
const _key = this.scheme.options.refreshToken.prefix + this.scheme.name
6669

67-
return this.$auth.$storage.getUniversal(_key)
70+
return this.$storage.getUniversal(_key)
6871
}
6972

7073
set (tokenValue) {
71-
const refreshToken = addTokenPrefix(tokenValue, this.$auth.strategy.options.refreshToken.type)
74+
const refreshToken = addTokenPrefix(tokenValue, this.scheme.options.refreshToken.type)
7275

7376
this._setToken(refreshToken)
7477
this._updateExpiration(refreshToken)

0 commit comments

Comments
 (0)