Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

fix(authentication-client): Remove access token for fatal 400 errors #2894

Merged
merged 1 commit into from
Nov 25, 2022
Merged
Changes from all commits
Commits
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
52 changes: 51 additions & 1 deletion packages/authentication-client/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ export class AuthenticationClient {
})
}

/**
* Parse the access token or authentication error from the window location hash. Will remove it from the hash
* if found.
*
* @param location The window location
* @returns The access token if available, will throw an error if found, otherwise null
*/
getFromLocation(location: Location) {
const [accessToken, tokenRegex] = getMatch(location, this.options.locationKey)

Expand All @@ -102,10 +109,21 @@ export class AuthenticationClient {
return Promise.resolve(null)
}

/**
* Set the access token in storage.
*
* @param accessToken The access token to set
* @returns
*/
setAccessToken(accessToken: string) {
return this.storage.setItem(this.options.storageKey, accessToken)
}

/**
* Returns the access token from storage or the window location hash.
*
* @returns The access token from storage or location hash
*/
getAccessToken(): Promise<string | null> {
return this.storage.getItem(this.options.storageKey).then((accessToken: string) => {
if (!accessToken && typeof window !== 'undefined' && window.location) {
Expand All @@ -116,10 +134,19 @@ export class AuthenticationClient {
})
}

/**
* Remove the access token from storage
* @returns The removed access token
*/
removeAccessToken() {
return this.storage.removeItem(this.options.storageKey)
}

/**
* Reset the internal authentication state. Usually not necessary to call directly.
*
* @returns null
*/
reset() {
this.app.set('authentication', null)
this.authenticated = false
Expand All @@ -128,7 +155,9 @@ export class AuthenticationClient {
}

handleError(error: FeathersError, type: 'authenticate' | 'logout') {
if (error.code === 401 || error.code === 403) {
// For NotAuthenticated, PaymentError, Forbidden, NotFound, MethodNotAllowed, NotAcceptable
// errors, remove the access token
if (error.code > 400 && error.code < 408) {
const promise = this.removeAccessToken().then(() => this.reset())

return type === 'logout' ? promise : promise.then(() => Promise.reject(error))
Expand All @@ -137,6 +166,14 @@ export class AuthenticationClient {
return Promise.reject(error)
}

/**
* Try to reauthenticate using the token from storage. Will do nothing if already authenticated unless
* `force` is true.
*
* @param force force reauthentication with the server
* @param strategy The name of the strategy to use. Defaults to `options.jwtStrategy`
* @returns The reauthentication result
*/
reAuthenticate(force = false, strategy?: string): Promise<AuthenticationResult> {
// Either returns the authentication state or
// tries to re-authenticate with the stored JWT and strategy
Expand All @@ -159,6 +196,13 @@ export class AuthenticationClient {
return authPromise
}

/**
* Authenticate using a specific strategy and data.
*
* @param authentication The authentication data
* @param params Additional parameters
* @returns The authentication result
*/
authenticate(authentication?: AuthenticationRequest, params?: Params): Promise<AuthenticationResult> {
if (!authentication) {
return this.reAuthenticate()
Expand All @@ -182,6 +226,12 @@ export class AuthenticationClient {
return promise
}

/**
* Log out the current user and remove their token. Will do nothing
* if not authenticated.
*
* @returns The log out result.
*/
logout(): Promise<AuthenticationResult | null> {
return Promise.resolve(this.app.get('authentication'))
.then(() =>
Expand Down