Skip to content
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

feat(local): add refresh token #325

Closed
wants to merge 89 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
6589dd8
fix: remove extra boom dependency
pi0 Nov 1, 2018
0894365
chore(release): 4.5.3
pi0 Nov 1, 2018
8488d31
Add refresh to local strategy
JoaoPedroAS51 Feb 27, 2019
a629570
Add refresh to local strategy
JoaoPedroAS51 Feb 27, 2019
4f666cf
Allow propertyName to be nested
JoaoPedroAS51 Feb 27, 2019
50648f9
Use getProp instead of array loop to allow propertyName to be nested
JoaoPedroAS51 Feb 27, 2019
dd7f881
Fix tokenCreatedAt, refreshToken and tokenExpiration using getProp. S…
JoaoPedroAS51 Feb 27, 2019
7fe6a48
Fix missing () in Date.now() of tokenCreatedAt
JoaoPedroAS51 Feb 27, 2019
b762791
Fix tokenExpiration time
JoaoPedroAS51 Feb 28, 2019
8f55532
Set _updateTokens and _tokenRefresh as async methods to avoid error 401
JoaoPedroAS51 Feb 28, 2019
0928f4d
Prevent token refresh on mount if user is not logged in
JoaoPedroAS51 Feb 28, 2019
f8a4116
Fix prevention of token refresh on mount if user is not logged in
JoaoPedroAS51 Feb 28, 2019
b4f8a7a
Add axios interceptor on mount to retry requests that return error 40…
JoaoPedroAS51 Feb 28, 2019
1f42e68
Add setTimeout to wait 1s before refresh the token on mount
JoaoPedroAS51 Feb 28, 2019
0f30fc5
Fix token var
JoaoPedroAS51 Feb 28, 2019
a24654d
Fix the "if" on request retry after error 401
JoaoPedroAS51 Mar 1, 2019
deb7447
Fix loggedIn check on mount
JoaoPedroAS51 Mar 15, 2019
f4ea4a6
Update axios interceptor to use onError method
JoaoPedroAS51 Mar 15, 2019
8be4049
Update function _tokenRefresh to use $auth.requestWith instead of $ax…
JoaoPedroAS51 Mar 15, 2019
0395d58
Now the token only refresh in the client side. This prevent error in …
JoaoPedroAS51 Mar 15, 2019
c60ae6f
Automatically logout locally on mount if the token has already expired.
JoaoPedroAS51 Mar 30, 2019
5f01bb9
Merge branch 'dev' into develop
JoaoPedroAS51 Mar 30, 2019
daa09f8
Fix merge with dev branch
JoaoPedroAS51 Mar 30, 2019
828d5b5
Fix merge with dev branch
JoaoPedroAS51 Mar 30, 2019
e977218
Merge branch 'dev' into develop
JoaoPedroAS51 Apr 3, 2019
47eecc9
Add setUserToken that was deleted on merge
JoaoPedroAS51 Apr 3, 2019
b330486
feat(refresh token): add defaultExpirationTime property (default 1800)
JoaoPedroAS51 Apr 18, 2019
834c07c
Merge remote-tracking branch 'origin/develop' into develop
JoaoPedroAS51 Apr 18, 2019
1a032bf
feat(refresh token): add grantType property (default: 'refresh_token')
JoaoPedroAS51 Apr 24, 2019
b95507f
feat(refresh token): add customizable payload properties
JoaoPedroAS51 Apr 24, 2019
885c0ee
fix(refresh token): only logout locally, if refresh request has failed
JoaoPedroAS51 Apr 26, 2019
7fbf407
fix(refresh token): only set user data if the request has not failed
JoaoPedroAS51 Apr 26, 2019
e1bb2a1
feat(refresh token): add system to refresh the token on failed reques…
JoaoPedroAS51 Apr 26, 2019
a8745b3
feat(refresh token): allow to remove client id and grant type from pa…
JoaoPedroAS51 May 21, 2019
fc1d1de
fix(refresh token): fix grantType property
JoaoPedroAS51 May 21, 2019
b900676
feat(logout): add clientId to logout payload
JoaoPedroAS51 May 22, 2019
81f7640
refactor(local strategy): rename property "client_id" to "clientId" a…
JoaoPedroAS51 May 22, 2019
f6b386e
feat(local strategy): add clientId to $auth instance
JoaoPedroAS51 May 22, 2019
bdd0afb
feat(logout): add customizable payload properties to logout endpoint
JoaoPedroAS51 May 22, 2019
227d4c3
fix(local strategy): remove clientId from config
JoaoPedroAS51 May 22, 2019
676bf8a
fix(local strategy): improve client id
JoaoPedroAS51 May 22, 2019
a768258
fix(local strategy): add default value of clientId
JoaoPedroAS51 May 22, 2019
0d55775
Merge pull request #7 from JoaoPedroAS51/feat/add-clientid-to-instanc…
JoaoPedroAS51 May 22, 2019
b98996d
refactor(defaults): fix clientId order
JoaoPedroAS51 May 22, 2019
c1b4795
fix(refresh token): clear the failed request queue on logout locally
JoaoPedroAS51 May 22, 2019
57d185f
fix(refresh token): prevent the token from being refreshed twice
JoaoPedroAS51 May 22, 2019
686eea1
fix(refresh token): set isRefreshing to false on logout locally
JoaoPedroAS51 May 22, 2019
bd78374
refactor(schemes): move refresh system to its own scheme
JoaoPedroAS51 May 23, 2019
fefe50a
fix: copy local scheme
JoaoPedroAS51 May 23, 2019
4ff875e
revert: "fix: copy local scheme"
JoaoPedroAS51 May 23, 2019
aa44ac2
fix: copy local scheme when using refresh scheme
JoaoPedroAS51 May 23, 2019
70ff2b2
fix(refresh scheme): remove global vars that have already been define…
JoaoPedroAS51 May 23, 2019
0d482f3
fix(refresh scheme): fix logout endpoint
JoaoPedroAS51 May 23, 2019
aa1c3a6
refactor(refresh scheme): move expiration and refresh helpers to refr…
JoaoPedroAS51 May 23, 2019
da695bc
fix(local scheme): sync client id and add it to logout payload
JoaoPedroAS51 May 23, 2019
ef981d8
fix(refresh scheme): fix refresh token and expiration reset
JoaoPedroAS51 May 23, 2019
78a7dd3
fix(refresh scheme): remove refresh token and expiration reset from c…
JoaoPedroAS51 May 23, 2019
de3ce4a
fix(refresh scheme): fix refresh token and expiration prefix
JoaoPedroAS51 May 23, 2019
d88e241
fix(refresh scheme): fix refresh token endpoint
JoaoPedroAS51 May 23, 2019
47a226b
fix(refresh scheme): overwrite mounted method
JoaoPedroAS51 May 23, 2019
1ff0439
fix(login scheme): fix client id on login
JoaoPedroAS51 May 23, 2019
10b4086
fix(login scheme): fix token on login
JoaoPedroAS51 May 23, 2019
b6b638a
refactor(refresh scheme): remove unnecessary check typeof
JoaoPedroAS51 May 23, 2019
226e6d8
refactor(refresh scheme): change const to var (accessToken)
JoaoPedroAS51 May 23, 2019
e49a943
refactor(refresh scheme): change var to var (token and headerToken)
JoaoPedroAS51 May 23, 2019
d91078a
refactor(refresh scheme): change "self" to "this" in arrow function
JoaoPedroAS51 May 23, 2019
24eba6c
refactor(refresh scheme): apply prefixes of token expiration and refr…
JoaoPedroAS51 May 24, 2019
b78010a
fix(core): fix clientId state
JoaoPedroAS51 May 24, 2019
85ffb2d
feat: add remember system
JoaoPedroAS51 May 24, 2019
6c3dedf
revert(demo): fix accidental changes in demo files
JoaoPedroAS51 May 24, 2019
a8860bd
refactor(core): remove console log
JoaoPedroAS51 May 24, 2019
67934bd
refactor(remember): use cookie.expires instead of rememberFor and mak…
JoaoPedroAS51 May 24, 2019
8c0de50
fix(local scheme): fix token value in setUserToken
JoaoPedroAS51 May 24, 2019
0e87606
Merge pull request #8 from JoaoPedroAS51/refactor/refresh-scheme
JoaoPedroAS51 May 24, 2019
50bbf25
Merge branch 'dev' into pr/JoaoPedroAS51/325
May 24, 2019
5f1b65a
sync with dev (4.5.2)
May 24, 2019
4d9660e
remove uncessesary async
May 24, 2019
76695c3
don't overload login signuture
May 24, 2019
230ff6f
refactor: call getProp once
May 24, 2019
270f138
refactors on local.js
May 24, 2019
fd89224
simplify logout
May 24, 2019
8c3bedd
fix(local scheme): fix payload properties of logout endpoint
JoaoPedroAS51 May 25, 2019
66a6ea9
refactor(refresh scheme): simplify logout
JoaoPedroAS51 May 25, 2019
ba738b3
refactor(refresh scheme): don't overload login signature
JoaoPedroAS51 May 25, 2019
18f88e2
refactor(refresh scheme): import getProp from utilities
JoaoPedroAS51 May 25, 2019
1b6ad4d
refactor(refresh scheme): remove unnecessary check in fetchUser
JoaoPedroAS51 May 25, 2019
5d423a0
refactor: remove unnecessary const
JoaoPedroAS51 May 25, 2019
d8477b9
feat(utilities): add function getRealName
JoaoPedroAS51 May 25, 2019
ca950e4
refactor: move extra options of endpoints to scheme
JoaoPedroAS51 May 25, 2019
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
74 changes: 56 additions & 18 deletions lib/core/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default class Auth {
this._redirectListeners = []

// Storage & State
options.initialState = { user: null, loggedIn: false }
options.initialState = { user: null, loggedIn: false, remember: false, clientId: null }
const storage = new Storage(ctx, options)

this.$storage = storage
Expand Down Expand Up @@ -174,8 +174,9 @@ export default class Auth {
reset () {
if (!this.strategy.reset) {
this.setUser(false)
this.setClientId(this.$state.strategy, false)
this.setToken(this.$state.strategy, false)
this.setRefreshToken(this.$state.strategy, false)
this.setRemember(this.$state.strategy, undefined)
return Promise.resolve()
}

Expand All @@ -195,36 +196,67 @@ export default class Auth {
return this.$storage.getUniversal(_key)
}

setToken (strategy, token) {
setToken (strategy, token, options) {
const _key = this.options.token.prefix + strategy

return this.$storage.setUniversal(_key, token)
return this.$storage.setUniversal(_key, token, options)
}

syncToken (strategy) {
syncToken (strategy, options) {
const _key = this.options.token.prefix + strategy

return this.$storage.syncUniversal(_key, undefined, options)
}

// ---------------------------------------------------------------
// Client ID helpers
// ---------------------------------------------------------------

getClientId (strategy) {
const _key = this.options.client_id.prefix + strategy

return this.$storage.getUniversal(_key)
}

setClientId (strategy, clientId, options) {
const _key = this.options.client_id.prefix + strategy

this.$storage.setState('clientId', clientId)

return this.$storage.setUniversal(_key, clientId, options)
}

syncClientId (strategy) {
const _key = this.options.client_id.prefix + strategy

return this.$storage.syncUniversal(_key)
}

// ---------------------------------------------------------------
// Refresh token helpers
// Remember helpers
// ---------------------------------------------------------------

getRefreshToken (strategy) {
const _key = this.options.refresh_token.prefix + strategy
getRemember (strategy) {
const _key = this.options.remember.prefix + strategy

return this.$storage.getUniversal(_key)
}

setRefreshToken (strategy, refreshToken) {
const _key = this.options.refresh_token.prefix + strategy
setRemember (strategy, remember) {
const _key = this.options.remember.prefix + strategy
const options = { expires: null }

if (remember === true || remember === undefined) {
options.expires = this.options.cookie.options.expires
}

this.$storage.setState('remember', remember || false)

return this.$storage.setUniversal(_key, refreshToken)
return this.$storage.setUniversal(_key, remember, options)
}

syncRefreshToken (strategy) {
const _key = this.options.refresh_token.prefix + strategy
syncRemember (strategy) {
const _key = this.options.remember.prefix + strategy

return this.$storage.syncUniversal(_key)
}
Expand All @@ -241,6 +273,14 @@ export default class Auth {
return this.$state.loggedIn
}

get remember () {
return this.$state.remember
}

get clientId () {
return this.$state.clientId
}

fetchUserOnce () {
if (!this.$state.user) {
return this.fetchUser(...arguments)
Expand All @@ -250,6 +290,8 @@ export default class Auth {

setUser (user) {
this.$storage.setState('loggedIn', Boolean(user))
this.$storage.setState('remember', this.getRemember(this.$state.strategy))
this.$storage.setState('clientId', this.getClientId(this.$state.strategy))
this.$storage.setState('user', user)
}

Expand All @@ -270,11 +312,7 @@ export default class Auth {
return this.ctx.app.$axios
.request(_endpoint)
.then(response => {
if (_endpoint.propertyName) {
return getProp(response.data, _endpoint.propertyName)
} else {
return response.data
}
return response.data
})
.catch(error => {
// Call all error handlers
Expand Down
8 changes: 4 additions & 4 deletions lib/core/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ export default class Storage {
// Universal
// ------------------------------------

setUniversal (key, value, isJson) {
setUniversal (key, value, options, isJson) {
// Local state
this.setState(key, value)

// Cookies
this.setCookie(key, value)
this.setCookie(key, value, options)

// Local Storage
this.setLocalStorage(key, value, isJson)
Expand All @@ -47,15 +47,15 @@ export default class Storage {
return value
}

syncUniversal (key, defaultValue, isJson) {
syncUniversal (key, defaultValue, options, isJson) {
let value = this.getUniversal(key, isJson)

if (isUnset(value) && isSet(defaultValue)) {
value = defaultValue
}

if (isSet(value)) {
this.setUniversal(key, value)
this.setUniversal(key, value, options)
}

return value
Expand Down
6 changes: 6 additions & 0 deletions lib/core/utilities.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import dotProp from 'dotprop'

export const getProp = (obj, propName) => propName ? dotProp(obj, propName) : obj

export const getRealName = (propName) => propName.split('.').pop()

export const isUnset = o => typeof o === 'undefined' || o === null
export const isSet = o => !isUnset(o)

Expand Down
17 changes: 14 additions & 3 deletions lib/module/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,16 @@ module.exports = {
prefix: '_token.'
},

// -- Refresh token --
// -- Client ID --

refresh_token: {
prefix: '_refresh_token.'
client_id: {
prefix: '_client_id.'
},

// -- Remember --

remember: {
prefix: '_remember.'
},

// -- Strategies --
Expand All @@ -67,6 +73,11 @@ module.exports = {
method: 'post',
propertyName: 'token'
},
refresh: {
url: '/api/auth/refresh',
method: 'post',
propertyName: 'token'
},
logout: {
url: '/api/auth/logout',
method: 'post'
Expand Down
7 changes: 7 additions & 0 deletions lib/module/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,13 @@ function processStrategies (options) {
fileName: join('auth', 'schemes', schemeName)
})

if (schemeName === 'refresh.js') {
this.addTemplate({
src: schemeSrc.replace('refresh.js', 'local.js'),
fileName: join('auth', 'schemes', schemeName.replace('refresh.js', 'local.js'))
})
}

// Remove unnecessary fields
delete strategy._scheme
delete strategy._provider
Expand Down
75 changes: 52 additions & 23 deletions lib/schemes/local.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { getProp } from '../utilities'
import { getRealName } from '../utilities'

export default class LocalScheme {
constructor (auth, options) {
this.$auth = auth
Expand All @@ -24,49 +27,67 @@ export default class LocalScheme {
if (this.options.tokenRequired) {
const token = this.$auth.syncToken(this.name)
this._setToken(token)
this.$auth.syncClientId(this.name)
this.$auth.syncRemember(this.name)
}

return this.$auth.fetchUserOnce()
}

async login (endpoint) {
async login (endpoint, remember) {
if (!this.options.endpoints.login) {
return
}

// Ditch any leftover local tokens before attempting to log in
await this._logoutLocally()

const result = await this.$auth.request(
endpoint,
this.options.endpoints.login
)
// Make login request
const loginResult = await this.$auth.request(endpoint, this.options.endpoints.login)

if (this.options.tokenRequired) {
const token = this.options.tokenType
? this.options.tokenType + ' ' + result
: result
// RememberMe
if (remember !== undefined) {
this.$auth.setRemember(this.name, remember)
}
const cookieOptions = { expires: null }
if (this.$auth.getRemember(this.name)) {
cookieOptions.expires = this.$auth.options.cookie.options.expires
}

this.$auth.setToken(this.name, token)
// Update Token
if (this.options.tokenRequired) {
let token = getProp(loginResult, this.options.endpoints.login.propertyName)
if (this.options.tokenType) {
token = this.options.tokenType + ' ' + token
}
this.$auth.setToken(this.name, token, cookieOptions)
this._setToken(token)
}

// Update clientId
const clientId = getProp(loginResult, this.options.clientIdProperty)
if (clientId) {
this.$auth.setClientId(this.name, clientId, cookieOptions)
}

// Fetch user
return this.fetchUser()
}

async setUserToken (tokenValue) {
async setUserToken (token) {
// Ditch any leftover local tokens before attempting to log in
await this._logoutLocally()

// Update token
if (this.options.tokenRequired) {
const token = this.options.tokenType
? this.options.tokenType + ' ' + tokenValue
: tokenValue

if (this.options.tokenType) {
token = this.options.tokenType + ' ' + token
}
this.$auth.setToken(this.name, token)
this._setToken(token)
}

// Fetch user
return this.fetchUser()
}

Expand All @@ -76,24 +97,31 @@ export default class LocalScheme {
return
}

// User endpoint is disabled.
// User endpoint is disabled
if (!this.options.endpoints.user) {
this.$auth.setUser({})
return
}

// Try to fetch user and then set
const user = await this.$auth.requestWith(
this.name,
endpoint,
this.options.endpoints.user
)
let user = await this.$auth.requestWith(this.name, endpoint, this.options.endpoints.user)
user = getProp(user, this.options.endpoints.user.propertyName)

this.$auth.setUser(user)
}

async logout (endpoint) {
async logout (endpoint = {}) {
// Only connect to logout endpoint if it's configured
if (this.options.endpoints.logout) {
// Only add client id to payload if enabled
const clientId = getRealName(this.options.clientIdProperty)
if (clientId) {
if (!endpoint.data) {
endpoint.data = {}
}
endpoint.data[clientId] = this.$auth.getClientId(this.name)
}

await this.$auth
.requestWith(this.name, endpoint, this.options.endpoints.logout)
.catch(() => { })
Expand All @@ -116,5 +144,6 @@ const DEFAULTS = {
tokenRequired: true,
tokenType: 'Bearer',
globalToken: true,
tokenName: 'Authorization'
tokenName: 'Authorization',
clientIdProperty: 'client_id'
}
Loading