UNPKG

@feathersjs/authentication-client

Version:

The authentication plugin for feathers-client

184 lines 6.89 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AuthenticationClient = void 0; const errors_1 = require("@feathersjs/errors"); const storage_1 = require("./storage"); class OauthError extends errors_1.FeathersError { constructor(message, data) { super(message, 'OauthError', 401, 'oauth-error', data); } } const getMatch = (location, key) => { const regex = new RegExp(`(?:\&?)${key}=([^&]*)`); const match = location.hash ? location.hash.match(regex) : null; if (match !== null) { const [, value] = match; return [value, regex]; } return [null, regex]; }; class AuthenticationClient { constructor(app, options) { const socket = app.io; const storage = new storage_1.StorageWrapper(app.get('storage') || options.storage); this.app = app; this.options = options; this.authenticated = false; this.app.set('storage', storage); if (socket) { this.handleSocket(socket); } } get service() { return this.app.service(this.options.path); } get storage() { return this.app.get('storage'); } handleSocket(socket) { // When the socket disconnects and we are still authenticated, try to reauthenticate right away // the websocket connection will handle timeouts and retries socket.on('disconnect', () => { if (this.authenticated) { this.reAuthenticate(true); } }); } /** * 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) { const [accessToken, tokenRegex] = getMatch(location, this.options.locationKey); if (accessToken !== null) { location.hash = location.hash.replace(tokenRegex, ''); return Promise.resolve(accessToken); } const [message, errorRegex] = getMatch(location, this.options.locationErrorKey); if (message !== null) { location.hash = location.hash.replace(errorRegex, ''); return Promise.reject(new OauthError(decodeURIComponent(message))); } return Promise.resolve(null); } /** * Set the access token in storage. * * @param accessToken The access token to set * @returns */ setAccessToken(accessToken) { 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() { return this.storage.getItem(this.options.storageKey).then((accessToken) => { if (!accessToken && typeof window !== 'undefined' && window.location) { return this.getFromLocation(window.location); } return accessToken || null; }); } /** * 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; return Promise.resolve(null); } handleError(error, type) { // 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)); } return this.reset().then(() => 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` * @param authParams Additional authentication parameters * @returns The reauthentication result */ reAuthenticate(force = false, strategy, authParams) { // Either returns the authentication state or // tries to re-authenticate with the stored JWT and strategy let authPromise = this.app.get('authentication'); if (!authPromise || force === true) { authPromise = this.getAccessToken().then((accessToken) => { if (!accessToken) { return this.handleError(new errors_1.NotAuthenticated('No accessToken found in storage'), 'authenticate'); } return this.authenticate({ strategy: strategy || this.options.jwtStrategy, accessToken }, authParams); }); this.app.set('authentication', authPromise); } return authPromise; } /** * Authenticate using a specific strategy and data. * * @param authentication The authentication data * @param params Additional parameters * @returns The authentication result */ authenticate(authentication, params) { if (!authentication) { return this.reAuthenticate(); } const promise = this.service .create(authentication, params) .then((authResult) => { const { accessToken } = authResult; this.authenticated = true; this.app.emit('login', authResult); this.app.emit('authenticated', authResult); return this.setAccessToken(accessToken).then(() => authResult); }) .catch((error) => this.handleError(error, 'authenticate')); this.app.set('authentication', promise); return promise; } /** * Log out the current user and remove their token. Will do nothing * if not authenticated. * * @returns The log out result. */ logout() { return Promise.resolve(this.app.get('authentication')) .then(() => this.service.remove(null).then((authResult) => this.removeAccessToken() .then(() => this.reset()) .then(() => { this.app.emit('logout', authResult); return authResult; }))) .catch((error) => this.handleError(error, 'logout')); } } exports.AuthenticationClient = AuthenticationClient; //# sourceMappingURL=core.js.map