@nebular/auth
Version:
@nebular/auth
1,458 lines (1,444 loc) • 154 kB
JavaScript
import * as i0 from '@angular/core';
import { InjectionToken, Injectable, Inject, Component, ChangeDetectionStrategy, Injector, NgModule } from '@angular/core';
import * as i3 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i2 from '@angular/router';
import { RouterModule } from '@angular/router';
import * as i5 from '@angular/forms';
import { FormsModule } from '@angular/forms';
import * as i4 from '@nebular/theme';
import { NB_WINDOW, NbLayoutModule, NbCardModule, NbCheckboxModule, NbAlertModule, NbInputModule, NbButtonModule, NbIconModule } from '@nebular/theme';
import { BehaviorSubject, of, Subject } from 'rxjs';
import { filter, share, map, switchMap, delay, catchError, takeUntil } from 'rxjs/operators';
import * as i1 from '@angular/common/http';
import { HttpResponse, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
const socialLinks = [];
const defaultAuthOptions = {
strategies: [],
forms: {
login: {
redirectDelay: 500, // delay before redirect after a successful login, while success message is shown to the user
strategy: 'email', // provider id key. If you have multiple strategies, or what to use your own
rememberMe: true, // whether to show or not the `rememberMe` checkbox
showMessages: {
success: true,
error: true,
},
socialLinks: socialLinks, // social links at the bottom of a page
},
register: {
redirectDelay: 500,
strategy: 'email',
showMessages: {
success: true,
error: true,
},
terms: true,
socialLinks: socialLinks,
},
requestPassword: {
redirectDelay: 500,
strategy: 'email',
showMessages: {
success: true,
error: true,
},
socialLinks: socialLinks,
},
resetPassword: {
redirectDelay: 500,
strategy: 'email',
showMessages: {
success: true,
error: true,
},
socialLinks: socialLinks,
},
logout: {
redirectDelay: 500,
strategy: 'email',
},
validation: {
password: {
required: true,
minLength: 4,
maxLength: 50,
},
email: {
required: true,
},
fullName: {
required: false,
minLength: 4,
maxLength: 50,
},
},
},
};
const NB_AUTH_OPTIONS = new InjectionToken('Nebular Auth Options');
const NB_AUTH_USER_OPTIONS = new InjectionToken('Nebular User Auth Options');
const NB_AUTH_STRATEGIES = new InjectionToken('Nebular Auth Strategies');
const NB_AUTH_TOKENS = new InjectionToken('Nebular Auth Tokens');
const NB_AUTH_INTERCEPTOR_HEADER = new InjectionToken('Nebular Simple Interceptor Header');
const NB_AUTH_TOKEN_INTERCEPTOR_FILTER = new InjectionToken('Nebular Interceptor Filter');
/**
* Extending object that entered in first argument.
*
* Returns extended object or false if have no target object or incorrect type.
*
* If you wish to clone source object (without modify it), just use empty new
* object as first argument, like this:
* deepExtend({}, yourObj_1, [yourObj_N]);
*/
const deepExtend = function (...objects) {
if (arguments.length < 1 || typeof arguments[0] !== 'object') {
return false;
}
if (arguments.length < 2) {
return arguments[0];
}
const target = arguments[0];
// convert arguments to array and cut off target object
const args = Array.prototype.slice.call(arguments, 1);
let val, src;
args.forEach(function (obj) {
// skip argument if it is array or isn't object
if (typeof obj !== 'object' || Array.isArray(obj)) {
return;
}
Object.keys(obj).forEach(function (key) {
src = target[key]; // source value
val = obj[key]; // new value
// recursion prevention
if (val === target) {
return;
/**
* if new value isn't object then just overwrite by new value
* instead of extending.
*/
}
else if (typeof val !== 'object' || val === null) {
target[key] = val;
return;
// just clone arrays (and recursive clone objects inside)
}
else if (Array.isArray(val)) {
target[key] = deepCloneArray(val);
return;
// custom cloning and overwrite for specific objects
}
else if (isSpecificValue(val)) {
target[key] = cloneSpecificValue(val);
return;
// overwrite by new value if source isn't object or array
}
else if (typeof src !== 'object' || src === null || Array.isArray(src)) {
target[key] = deepExtend({}, val);
return;
// source value and new value is objects both, extending...
}
else {
target[key] = deepExtend(src, val);
return;
}
});
});
return target;
};
function isSpecificValue(val) {
return (val instanceof Date
|| val instanceof RegExp) ? true : false;
}
function cloneSpecificValue(val) {
if (val instanceof Date) {
return new Date(val.getTime());
}
else if (val instanceof RegExp) {
return new RegExp(val);
}
else {
throw new Error('cloneSpecificValue: Unexpected situation');
}
}
/**
* Recursive cloning array.
*/
function deepCloneArray(arr) {
const clone = [];
arr.forEach(function (item, index) {
if (typeof item === 'object' && item !== null) {
if (Array.isArray(item)) {
clone[index] = deepCloneArray(item);
}
else if (isSpecificValue(item)) {
clone[index] = cloneSpecificValue(item);
}
else {
clone[index] = deepExtend({}, item);
}
}
else {
clone[index] = item;
}
});
return clone;
}
// getDeepFromObject({result: {data: 1}}, 'result.data', 2); // returns 1
function getDeepFromObject(object = {}, name, defaultValue) {
const keys = name.split('.');
// clone the object
let level = deepExtend({}, object || {});
keys.forEach((k) => {
if (level && typeof level[k] !== 'undefined') {
level = level[k];
}
else {
level = undefined;
}
});
return typeof level === 'undefined' ? defaultValue : level;
}
function urlBase64Decode(str) {
let output = str.replace(/-/g, '+').replace(/_/g, '/');
switch (output.length % 4) {
case 0: {
break;
}
case 2: {
output += '==';
break;
}
case 3: {
output += '=';
break;
}
default: {
throw new Error('Illegal base64url string!');
}
}
return b64DecodeUnicode(output);
}
function b64decode(str) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
let output = '';
str = String(str).replace(/=+$/, '');
if (str.length % 4 === 1) {
throw new Error(`'atob' failed: The string to be decoded is not correctly encoded.`);
}
for (
// initialize result and counters
let bc = 0, bs, buffer, idx = 0;
// get next character
buffer = str.charAt(idx++);
// character found in table? initialize bit storage and add its ascii value;
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
// and if not first of each 4 characters,
// convert the first 8 bits to one ascii character
bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0) {
// try to find character in table (0-63, not found => -1)
buffer = chars.indexOf(buffer);
}
return output;
}
// https://developer.mozilla.org/en/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem
function b64DecodeUnicode(str) {
return decodeURIComponent(Array.prototype.map.call(b64decode(str), (c) => {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
}
class NbAuthToken {
constructor() {
this.payload = null;
}
getName() {
return this.constructor.NAME;
}
getPayload() {
return this.payload;
}
}
class NbAuthTokenNotFoundError extends Error {
constructor(message) {
super(message);
Object.setPrototypeOf(this, new.target.prototype);
}
}
class NbAuthIllegalTokenError extends Error {
constructor(message) {
super(message);
Object.setPrototypeOf(this, new.target.prototype);
}
}
class NbAuthEmptyTokenError extends NbAuthIllegalTokenError {
constructor(message) {
super(message);
Object.setPrototypeOf(this, new.target.prototype);
}
}
class NbAuthIllegalJWTTokenError extends NbAuthIllegalTokenError {
constructor(message) {
super(message);
Object.setPrototypeOf(this, new.target.prototype);
}
}
function nbAuthCreateToken(tokenClass, token, ownerStrategyName, createdAt) {
return new tokenClass(token, ownerStrategyName, createdAt);
}
function decodeJwtPayload(payload) {
if (payload.length === 0) {
throw new NbAuthEmptyTokenError('Cannot extract from an empty payload.');
}
const parts = payload.split('.');
if (parts.length !== 3) {
throw new NbAuthIllegalJWTTokenError(`The payload ${payload} is not valid JWT payload and must consist of three parts.`);
}
let decoded;
try {
decoded = urlBase64Decode(parts[1]);
}
catch (e) {
throw new NbAuthIllegalJWTTokenError(`The payload ${payload} is not valid JWT payload and cannot be parsed.`);
}
if (!decoded) {
throw new NbAuthIllegalJWTTokenError(`The payload ${payload} is not valid JWT payload and cannot be decoded.`);
}
return JSON.parse(decoded);
}
/**
* Wrapper for simple (text) token
*/
class NbAuthSimpleToken extends NbAuthToken {
static { this.NAME = 'nb:auth:simple:token'; }
constructor(token, ownerStrategyName, createdAt) {
super();
this.token = token;
this.ownerStrategyName = ownerStrategyName;
this.createdAt = createdAt;
try {
this.parsePayload();
}
catch (err) {
if (!(err instanceof NbAuthTokenNotFoundError)) {
// token is present but has got a problem, including illegal
throw err;
}
}
this.createdAt = this.prepareCreatedAt(createdAt);
}
parsePayload() {
this.payload = null;
}
prepareCreatedAt(date) {
return date ? date : new Date();
}
/**
* Returns the token's creation date
* @returns {Date}
*/
getCreatedAt() {
return this.createdAt;
}
/**
* Returns the token value
* @returns string
*/
getValue() {
return this.token;
}
getOwnerStrategyName() {
return this.ownerStrategyName;
}
/**
* Is non empty and valid
* @returns {boolean}
*/
isValid() {
return !!this.getValue();
}
/**
* Validate value and convert to string, if value is not valid return empty string
* @returns {string}
*/
toString() {
return !!this.token ? this.token : '';
}
}
/**
* Wrapper for JWT token with additional methods.
*/
class NbAuthJWTToken extends NbAuthSimpleToken {
static { this.NAME = 'nb:auth:jwt:token'; }
/**
* for JWT token, the iat (issued at) field of the token payload contains the creation Date
*/
prepareCreatedAt(date) {
const decoded = this.getPayload();
return decoded && decoded.iat ? new Date(Number(decoded.iat) * 1000) : super.prepareCreatedAt(date);
}
/**
* Returns payload object
* @returns any
*/
parsePayload() {
if (!this.token) {
throw new NbAuthTokenNotFoundError('Token not found. ');
}
this.payload = decodeJwtPayload(this.token);
}
/**
* Returns expiration date
* @returns Date
*/
getTokenExpDate() {
const decoded = this.getPayload();
if (decoded && !decoded.hasOwnProperty('exp')) {
return null;
}
const date = new Date(0);
date.setUTCSeconds(decoded.exp); // 'cause jwt token are set in seconds
return date;
}
/**
* Is data expired
* @returns {boolean}
*/
isValid() {
return super.isValid() && (!this.getTokenExpDate() || new Date() < this.getTokenExpDate());
}
}
const prepareOAuth2Token = (data) => {
if (typeof data === 'string') {
try {
return JSON.parse(data);
}
catch (e) { }
}
return data;
};
/**
* Wrapper for OAuth2 token whose access_token is a JWT Token
*/
class NbAuthOAuth2Token extends NbAuthSimpleToken {
static { this.NAME = 'nb:auth:oauth2:token'; }
constructor(data = {}, ownerStrategyName, createdAt) {
// we may get it as string when retrieving from a storage
super(prepareOAuth2Token(data), ownerStrategyName, createdAt);
}
/**
* Returns the token value
* @returns string
*/
getValue() {
return this.token.access_token;
}
/**
* Returns the refresh token
* @returns string
*/
getRefreshToken() {
return this.token.refresh_token;
}
/**
* put refreshToken in the token payload
* @param refreshToken
*/
setRefreshToken(refreshToken) {
this.token.refresh_token = refreshToken;
}
/**
* Parses token payload
* @returns any
*/
parsePayload() {
if (!this.token) {
throw new NbAuthTokenNotFoundError('Token not found.');
}
else {
if (!Object.keys(this.token).length) {
throw new NbAuthEmptyTokenError('Cannot extract payload from an empty token.');
}
}
this.payload = this.token;
}
/**
* Returns the token type
* @returns string
*/
getType() {
return this.token.token_type;
}
/**
* Is data expired
* @returns {boolean}
*/
isValid() {
return super.isValid() && (!this.getTokenExpDate() || new Date() < this.getTokenExpDate());
}
/**
* Returns expiration date
* @returns Date
*/
getTokenExpDate() {
if (!this.token.hasOwnProperty('expires_in')) {
return null;
}
return new Date(this.createdAt.getTime() + Number(this.token.expires_in) * 1000);
}
/**
* Convert to string
* @returns {string}
*/
toString() {
return JSON.stringify(this.token);
}
}
/**
* Wrapper for OAuth2 token embedding JWT tokens
*/
class NbAuthOAuth2JWTToken extends NbAuthOAuth2Token {
static { this.NAME = 'nb:auth:oauth2:jwt:token'; }
parsePayload() {
super.parsePayload();
this.parseAccessTokenPayload();
}
parseAccessTokenPayload() {
const accessToken = this.getValue();
if (!accessToken) {
throw new NbAuthTokenNotFoundError('access_token key not found.');
}
this.accessTokenPayload = decodeJwtPayload(accessToken);
}
/**
* Returns access token payload
* @returns any
*/
getAccessTokenPayload() {
return this.accessTokenPayload;
}
/**
* for Oauth2 JWT token, the iat (issued at) field of the access_token payload
*/
prepareCreatedAt(date) {
const payload = this.accessTokenPayload;
return payload && payload.iat ? new Date(Number(payload.iat) * 1000) : super.prepareCreatedAt(date);
}
/**
* Is token valid
* @returns {boolean}
*/
isValid() {
return this.accessTokenPayload && super.isValid();
}
/**
* Returns expiration date :
* - exp if set,
* - super.getExpDate() otherwise
* @returns Date
*/
getTokenExpDate() {
if (this.accessTokenPayload && this.accessTokenPayload.hasOwnProperty('exp')) {
const date = new Date(0);
date.setUTCSeconds(this.accessTokenPayload.exp);
return date;
}
else {
return super.getTokenExpDate();
}
}
}
const NB_AUTH_FALLBACK_TOKEN = new InjectionToken('Nebular Auth Options');
/**
* Creates a token parcel which could be stored/restored
*/
class NbAuthTokenParceler {
constructor(fallbackClass, tokenClasses) {
this.fallbackClass = fallbackClass;
this.tokenClasses = tokenClasses;
}
wrap(token) {
return JSON.stringify({
name: token.getName(),
ownerStrategyName: token.getOwnerStrategyName(),
createdAt: token.getCreatedAt().getTime(),
value: token.toString(),
});
}
unwrap(value) {
let tokenClass = this.fallbackClass;
let tokenValue = '';
let tokenOwnerStrategyName = '';
let tokenCreatedAt = null;
const tokenPack = this.parseTokenPack(value);
if (tokenPack) {
tokenClass = this.getClassByName(tokenPack.name) || this.fallbackClass;
tokenValue = tokenPack.value;
tokenOwnerStrategyName = tokenPack.ownerStrategyName;
tokenCreatedAt = new Date(Number(tokenPack.createdAt));
}
return nbAuthCreateToken(tokenClass, tokenValue, tokenOwnerStrategyName, tokenCreatedAt);
}
// TODO: this could be moved to a separate token registry
getClassByName(name) {
return this.tokenClasses.find((tokenClass) => tokenClass.NAME === name);
}
parseTokenPack(value) {
try {
return JSON.parse(value);
}
catch (e) { }
return null;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: NbAuthTokenParceler, deps: [{ token: NB_AUTH_FALLBACK_TOKEN }, { token: NB_AUTH_TOKENS }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: NbAuthTokenParceler }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: NbAuthTokenParceler, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: undefined, decorators: [{
type: Inject,
args: [NB_AUTH_FALLBACK_TOKEN]
}] }, { type: undefined, decorators: [{
type: Inject,
args: [NB_AUTH_TOKENS]
}] }] });
class NbTokenStorage {
}
/**
* Service that uses browser localStorage as a storage.
*
* The token storage is provided into auth module the following way:
* ```ts
* { provide: NbTokenStorage, useClass: NbTokenLocalStorage },
* ```
*
* If you need to change the storage behaviour or provide your own - just extend your class from basic `NbTokenStorage`
* or `NbTokenLocalStorage` and provide in your `app.module`:
* ```ts
* { provide: NbTokenStorage, useClass: NbTokenCustomStorage },
* ```
*
*/
class NbTokenLocalStorage extends NbTokenStorage {
constructor(parceler) {
super();
this.parceler = parceler;
this.key = 'auth_app_token';
}
/**
* Returns token from localStorage
* @returns {NbAuthToken}
*/
get() {
const raw = localStorage.getItem(this.key);
return this.parceler.unwrap(raw);
}
/**
* Sets token to localStorage
* @param {NbAuthToken} token
*/
set(token) {
const raw = this.parceler.wrap(token);
localStorage.setItem(this.key, raw);
}
/**
* Clears token from localStorage
*/
clear() {
localStorage.removeItem(this.key);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: NbTokenLocalStorage, deps: [{ token: NbAuthTokenParceler }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: NbTokenLocalStorage }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: NbTokenLocalStorage, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: NbAuthTokenParceler }] });
/**
* Service that allows you to manage authentication token - get, set, clear and also listen to token changes over time.
*/
class NbTokenService {
constructor(tokenStorage) {
this.tokenStorage = tokenStorage;
this.token$ = new BehaviorSubject(null);
this.publishStoredToken();
}
/**
* Publishes token when it changes.
* @returns {Observable<NbAuthToken>}
*/
tokenChange() {
return this.token$
.pipe(filter(value => !!value), share());
}
/**
* Sets a token into the storage. This method is used by the NbAuthService automatically.
*
* @param {NbAuthToken} token
* @returns {Observable<any>}
*/
set(token) {
this.tokenStorage.set(token);
this.publishStoredToken();
return of(null);
}
/**
* Returns observable of current token
* @returns {Observable<NbAuthToken>}
*/
get() {
const token = this.tokenStorage.get();
return of(token);
}
/**
* Removes the token and published token value
*
* @returns {Observable<any>}
*/
clear() {
this.tokenStorage.clear();
this.publishStoredToken();
return of(null);
}
publishStoredToken() {
this.token$.next(this.tokenStorage.get());
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: NbTokenService, deps: [{ token: NbTokenStorage }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: NbTokenService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: NbTokenService, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: NbTokenStorage }] });
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
/**
* Common authentication service.
* Should be used to as an interlayer between UI Components and Auth Strategy.
*/
class NbAuthService {
constructor(tokenService, strategies) {
this.tokenService = tokenService;
this.strategies = strategies;
}
/**
* Retrieves current authenticated token stored
* @returns {Observable<any>}
*/
getToken() {
return this.tokenService.get();
}
/**
* Returns true if auth token is present in the token storage
* @returns {Observable<boolean>}
*/
isAuthenticated() {
return this.getToken()
.pipe(map((token) => token.isValid()));
}
/**
* Returns true if valid auth token is present in the token storage.
* If not, calls the strategy refreshToken, and returns isAuthenticated() if success, false otherwise
* @returns {Observable<boolean>}
*/
isAuthenticatedOrRefresh() {
return this.getToken()
.pipe(switchMap(token => {
if (token.getValue() && !token.isValid()) {
return this.refreshToken(token.getOwnerStrategyName(), token)
.pipe(switchMap(res => {
if (res.isSuccess()) {
return this.isAuthenticated();
}
else {
return of(false);
}
}));
}
else {
return of(token.isValid());
}
}));
}
/**
* Returns tokens stream
* @returns {Observable<NbAuthSimpleToken>}
*/
onTokenChange() {
return this.tokenService.tokenChange();
}
/**
* Returns authentication status stream
* @returns {Observable<boolean>}
*/
onAuthenticationChange() {
return this.onTokenChange()
.pipe(map((token) => token.isValid()));
}
/**
* Authenticates with the selected strategy
* Stores received token in the token storage
*
* Example:
* authenticate('email', {email: 'email@example.com', password: 'test'})
*
* @param strategyName
* @param data
* @returns {Observable<NbAuthResult>}
*/
authenticate(strategyName, data) {
return this.getStrategy(strategyName).authenticate(data)
.pipe(switchMap((result) => {
return this.processResultToken(result);
}));
}
/**
* Registers with the selected strategy
* Stores received token in the token storage
*
* Example:
* register('email', {email: 'email@example.com', name: 'Some Name', password: 'test'})
*
* @param strategyName
* @param data
* @returns {Observable<NbAuthResult>}
*/
register(strategyName, data) {
return this.getStrategy(strategyName).register(data)
.pipe(switchMap((result) => {
return this.processResultToken(result);
}));
}
/**
* Sign outs with the selected strategy
* Removes token from the token storage
*
* Example:
* logout('email')
*
* @param strategyName
* @returns {Observable<NbAuthResult>}
*/
logout(strategyName) {
return this.getStrategy(strategyName).logout()
.pipe(switchMap((result) => {
if (result.isSuccess()) {
this.tokenService.clear()
.pipe(map(() => result));
}
return of(result);
}));
}
/**
* Sends forgot password request to the selected strategy
*
* Example:
* requestPassword('email', {email: 'email@example.com'})
*
* @param strategyName
* @param data
* @returns {Observable<NbAuthResult>}
*/
requestPassword(strategyName, data) {
return this.getStrategy(strategyName).requestPassword(data);
}
/**
* Tries to reset password with the selected strategy
*
* Example:
* resetPassword('email', {newPassword: 'test'})
*
* @param strategyName
* @param data
* @returns {Observable<NbAuthResult>}
*/
resetPassword(strategyName, data) {
return this.getStrategy(strategyName).resetPassword(data);
}
/**
* Sends a refresh token request
* Stores received token in the token storage
*
* Example:
* refreshToken('email', {token: token})
*
* @param {string} strategyName
* @param data
* @returns {Observable<NbAuthResult>}
*/
refreshToken(strategyName, data) {
return this.getStrategy(strategyName).refreshToken(data)
.pipe(switchMap((result) => {
return this.processResultToken(result);
}));
}
/**
* Get registered strategy by name
*
* Example:
* getStrategy('email')
*
* @param {string} provider
* @returns {NbAbstractAuthProvider}
*/
getStrategy(strategyName) {
const found = this.strategies.find((strategy) => strategy.getName() === strategyName);
if (!found) {
throw new TypeError(`There is no Auth Strategy registered under '${strategyName}' name`);
}
return found;
}
processResultToken(result) {
if (result.isSuccess() && result.getToken()) {
return this.tokenService.set(result.getToken())
.pipe(map((token) => {
return result;
}));
}
return of(result);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: NbAuthService, deps: [{ token: NbTokenService }, { token: NB_AUTH_STRATEGIES }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: NbAuthService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: NbAuthService, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: NbTokenService }, { type: undefined, decorators: [{
type: Inject,
args: [NB_AUTH_STRATEGIES]
}] }] });
class NbAuthStrategy {
// we should keep this any and validation should be done in `register` method instead
// otherwise it won't be possible to pass an empty object
setOptions(options) {
this.options = deepExtend({}, this.defaultOptions, options);
}
getOption(key) {
return getDeepFromObject(this.options, key, null);
}
createToken(value, failWhenInvalidToken) {
const token = nbAuthCreateToken(this.getOption('token.class'), value, this.getName());
// At this point, nbAuthCreateToken failed with NbAuthIllegalTokenError which MUST be intercepted by strategies
// Or token is created. It MAY be created even if backend did not return any token, in this case it is !Valid
if (failWhenInvalidToken && !token.isValid()) {
// If we require a valid token (i.e. isValid), then we MUST throw NbAuthIllegalTokenError so that the strategies
// intercept it
throw new NbAuthIllegalTokenError('Token is empty or invalid.');
}
return token;
}
getName() {
return this.getOption('name');
}
createFailResponse(data) {
return new HttpResponse({ body: {}, status: 401 });
}
createSuccessResponse(data) {
return new HttpResponse({ body: {}, status: 200 });
}
getActionEndpoint(action) {
const actionEndpoint = this.getOption(`${action}.endpoint`);
const baseEndpoint = this.getOption('baseEndpoint');
return actionEndpoint ? baseEndpoint + actionEndpoint : '';
}
getHeaders() {
const customHeaders = this.getOption('headers') ?? {};
if (customHeaders instanceof HttpHeaders) {
return customHeaders;
}
let headers = new HttpHeaders();
Object.entries(customHeaders).forEach(([key, value]) => {
headers = headers.append(key, value);
});
return headers;
}
}
class NbAuthResult {
// TODO: better pass object
constructor(success, response, redirect, errors, messages, token = null) {
this.success = success;
this.response = response;
this.redirect = redirect;
this.errors = [];
this.messages = [];
this.errors = this.errors.concat([errors]);
if (errors instanceof Array) {
this.errors = errors;
}
this.messages = this.messages.concat([messages]);
if (messages instanceof Array) {
this.messages = messages;
}
this.token = token;
}
getResponse() {
return this.response;
}
getToken() {
return this.token;
}
getRedirect() {
return this.redirect;
}
getErrors() {
return this.errors.filter(val => !!val);
}
getMessages() {
return this.messages.filter(val => !!val);
}
isSuccess() {
return this.success;
}
isFailure() {
return !this.success;
}
}
class NbAuthStrategyOptions {
}
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
class NbDummyAuthStrategyOptions extends NbAuthStrategyOptions {
constructor() {
super(...arguments);
this.token = {
class: NbAuthSimpleToken,
};
this.delay = 1000;
this.alwaysFail = false;
}
}
const dummyStrategyOptions = new NbDummyAuthStrategyOptions();
/**
* Dummy auth strategy. Could be useful for auth setup when backend is not available yet.
*
*
* Strategy settings.
*
* ```ts
* export class NbDummyAuthStrategyOptions extends NbAuthStrategyOptions {
* name = 'dummy';
* token = {
* class: NbAuthSimpleToken,
* };
* delay? = 1000;
* alwaysFail? = false;
* }
* ```
*/
class NbDummyAuthStrategy extends NbAuthStrategy {
constructor() {
super(...arguments);
this.defaultOptions = dummyStrategyOptions;
}
static setup(options) {
return [NbDummyAuthStrategy, options];
}
authenticate(data) {
return of(this.createDummyResult(data)).pipe(delay(this.getOption('delay')));
}
register(data) {
return of(this.createDummyResult(data)).pipe(delay(this.getOption('delay')));
}
requestPassword(data) {
return of(this.createDummyResult(data)).pipe(delay(this.getOption('delay')));
}
resetPassword(data) {
return of(this.createDummyResult(data)).pipe(delay(this.getOption('delay')));
}
logout(data) {
return of(this.createDummyResult(data)).pipe(delay(this.getOption('delay')));
}
refreshToken(data) {
return of(this.createDummyResult(data)).pipe(delay(this.getOption('delay')));
}
createDummyResult(data) {
if (this.getOption('alwaysFail')) {
return new NbAuthResult(false, this.createFailResponse(data), null, ['Something went wrong.']);
}
try {
const token = this.createToken('test token', true);
return new NbAuthResult(true, this.createSuccessResponse(data), '/', [], ['Successfully logged in.'], token);
}
catch (err) {
return new NbAuthResult(false, this.createFailResponse(data), null, [err.message]);
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: NbDummyAuthStrategy, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: NbDummyAuthStrategy }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: NbDummyAuthStrategy, decorators: [{
type: Injectable
}] });
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
var NbOAuth2ResponseType;
(function (NbOAuth2ResponseType) {
NbOAuth2ResponseType["CODE"] = "code";
NbOAuth2ResponseType["TOKEN"] = "token";
})(NbOAuth2ResponseType || (NbOAuth2ResponseType = {}));
// TODO: client_credentials
var NbOAuth2GrantType;
(function (NbOAuth2GrantType) {
NbOAuth2GrantType["AUTHORIZATION_CODE"] = "authorization_code";
NbOAuth2GrantType["PASSWORD"] = "password";
NbOAuth2GrantType["REFRESH_TOKEN"] = "refresh_token";
})(NbOAuth2GrantType || (NbOAuth2GrantType = {}));
var NbOAuth2ClientAuthMethod;
(function (NbOAuth2ClientAuthMethod) {
NbOAuth2ClientAuthMethod["NONE"] = "none";
NbOAuth2ClientAuthMethod["BASIC"] = "basic";
NbOAuth2ClientAuthMethod["REQUEST_BODY"] = "request-body";
})(NbOAuth2ClientAuthMethod || (NbOAuth2ClientAuthMethod = {}));
class NbOAuth2AuthStrategyOptions extends NbAuthStrategyOptions {
constructor() {
super(...arguments);
this.baseEndpoint = '';
this.clientId = '';
this.clientSecret = '';
this.clientAuthMethod = NbOAuth2ClientAuthMethod.NONE;
this.redirect = {
success: '/',
failure: null,
};
this.defaultErrors = ['Something went wrong, please try again.'];
this.defaultMessages = ['You have been successfully authenticated.'];
this.authorize = {
endpoint: 'authorize',
responseType: NbOAuth2ResponseType.CODE,
requireValidToken: true,
};
this.token = {
endpoint: 'token',
grantType: NbOAuth2GrantType.AUTHORIZATION_CODE,
requireValidToken: true,
class: NbAuthOAuth2Token,
};
this.refresh = {
endpoint: 'token',
grantType: NbOAuth2GrantType.REFRESH_TOKEN,
requireValidToken: true,
};
}
}
const auth2StrategyOptions = new NbOAuth2AuthStrategyOptions();
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
/**
* OAuth2 authentication strategy.
*
* Strategy settings:
*
* ```ts
* export enum NbOAuth2ResponseType {
* CODE = 'code',
* TOKEN = 'token',
* }
*
* export enum NbOAuth2GrantType {
* AUTHORIZATION_CODE = 'authorization_code',
* PASSWORD = 'password',
* REFRESH_TOKEN = 'refresh_token',
* }
*
* export class NbOAuth2AuthStrategyOptions {
* name: string;
* baseEndpoint?: string = '';
* clientId: string = '';
* clientSecret: string = '';
* clientAuthMethod: string = NbOAuth2ClientAuthMethod.NONE;
* redirect?: { success?: string; failure?: string } = {
* success: '/',
* failure: null,
* };
* defaultErrors?: any[] = ['Something went wrong, please try again.'];
* defaultMessages?: any[] = ['You have been successfully authenticated.'];
* authorize?: {
* endpoint?: string;
* redirectUri?: string;
* responseType?: string;
* requireValidToken: true,
* scope?: string;
* state?: string;
* params?: { [key: string]: string };
* } = {
* endpoint: 'authorize',
* responseType: NbOAuth2ResponseType.CODE,
* };
* token?: {
* endpoint?: string;
* grantType?: string;
* requireValidToken: true,
* redirectUri?: string;
* scope?: string;
* class: NbAuthTokenClass,
* } = {
* endpoint: 'token',
* grantType: NbOAuth2GrantType.AUTHORIZATION_CODE,
* class: NbAuthOAuth2Token,
* };
* refresh?: {
* endpoint?: string;
* grantType?: string;
* scope?: string;
* requireValidToken: true,
* } = {
* endpoint: 'token',
* grantType: NbOAuth2GrantType.REFRESH_TOKEN,
* };
* }
* ```
*
*/
class NbOAuth2AuthStrategy extends NbAuthStrategy {
static setup(options) {
return [NbOAuth2AuthStrategy, options];
}
get responseType() {
return this.getOption('authorize.responseType');
}
get clientAuthMethod() {
return this.getOption('clientAuthMethod');
}
constructor(http, route, window) {
super();
this.http = http;
this.route = route;
this.window = window;
this.redirectResultHandlers = {
[NbOAuth2ResponseType.CODE]: () => {
return of(this.route.snapshot.queryParams).pipe(switchMap((params) => {
if (params.code) {
return this.requestToken(params.code);
}
return of(new NbAuthResult(false, params, this.getOption('redirect.failure'), this.getOption('defaultErrors'), []));
}));
},
[NbOAuth2ResponseType.TOKEN]: () => {
const module = 'authorize';
const requireValidToken = this.getOption(`${module}.requireValidToken`);
return of(this.route.snapshot.fragment).pipe(map((fragment) => this.parseHashAsQueryParams(fragment)), map((params) => {
if (!params.error) {
return new NbAuthResult(true, params, this.getOption('redirect.success'), [], this.getOption('defaultMessages'), this.createToken(params, requireValidToken));
}
return new NbAuthResult(false, params, this.getOption('redirect.failure'), this.getOption('defaultErrors'), []);
}), catchError((err) => {
const errors = [];
if (err instanceof NbAuthIllegalTokenError) {
errors.push(err.message);
}
else {
errors.push('Something went wrong.');
}
return of(new NbAuthResult(false, err, this.getOption('redirect.failure'), errors));
}));
},
};
this.redirectResults = {
[NbOAuth2ResponseType.CODE]: () => {
return of(this.route.snapshot.queryParams).pipe(map((params) => !!(params && (params.code || params.error))));
},
[NbOAuth2ResponseType.TOKEN]: () => {
return of(this.route.snapshot.fragment).pipe(map((fragment) => this.parseHashAsQueryParams(fragment)), map((params) => !!(params && (params.access_token || params.error))));
},
};
this.defaultOptions = auth2StrategyOptions;
}
authenticate(data) {
if (this.getOption('token.grantType') === NbOAuth2GrantType.PASSWORD) {
return this.passwordToken(data.email, data.password);
}
else {
return this.isRedirectResult().pipe(switchMap((result) => {
if (!result) {
this.authorizeRedirect();
return of(new NbAuthResult(true));
}
return this.getAuthorizationResult();
}));
}
}
getAuthorizationResult() {
const redirectResultHandler = this.redirectResultHandlers[this.responseType];
if (redirectResultHandler) {
return redirectResultHandler.call(this);
}
throw new Error(`'${this.responseType}' responseType is not supported,
only 'token' and 'code' are supported now`);
}
refreshToken(token) {
const module = 'refresh';
const url = this.getActionEndpoint(module);
const requireValidToken = this.getOption(`${module}.requireValidToken`);
return this.http.post(url, this.buildRefreshRequestData(token), { headers: this.getHeaders() }).pipe(map((res) => {
return new NbAuthResult(true, res, this.getOption('redirect.success'), [], this.getOption('defaultMessages'), this.createRefreshedToken(res, token, requireValidToken));
}), catchError((res) => this.handleResponseError(res)));
}
passwordToken(username, password) {
const module = 'token';
const url = this.getActionEndpoint(module);
const requireValidToken = this.getOption(`${module}.requireValidToken`);
return this.http.post(url, this.buildPasswordRequestData(username, password), { headers: this.getHeaders() }).pipe(map((res) => {
return new NbAuthResult(true, res, this.getOption('redirect.success'), [], this.getOption('defaultMessages'), this.createToken(res, requireValidToken));
}), catchError((res) => this.handleResponseError(res)));
}
authorizeRedirect() {
this.window.location.href = this.buildRedirectUrl();
}
isRedirectResult() {
return this.redirectResults[this.responseType].call(this);
}
requestToken(code) {
const module = 'token';
const url = this.getActionEndpoint(module);
const requireValidToken = this.getOption(`${module}.requireValidToken`);
return this.http.post(url, this.buildCodeRequestData(code), { headers: this.getHeaders() }).pipe(map((res) => {
return new NbAuthResult(true, res, this.getOption('redirect.success'), [], this.getOption('defaultMessages'), this.createToken(res, requireValidToken));
}), catchError((res) => this.handleResponseError(res)));
}
buildCodeRequestData(code) {
const params = {
grant_type: this.getOption('token.grantType'),
code: code,
redirect_uri: this.getOption('token.redirectUri'),
client_id: this.getOption('clientId'),
};
return this.urlEncodeParameters(this.cleanParams(this.addCredentialsToParams(params)));
}
buildRefreshRequestData(token) {
const params = {
grant_type: this.getOption('refresh.grantType'),
refresh_token: token.getRefreshToken(),
scope: this.getOption('refresh.scope'),
client_id: this.getOption('clientId'),
};
return this.urlEncodeParameters(this.cleanParams(this.addCredentialsToParams(params)));
}
buildPasswordRequestData(username, password) {
const params = {
grant_type: this.getOption('token.grantType'),
username: username,
password: password,
scope: this.getOption('token.scope'),
};
return this.urlEncodeParameters(this.cleanParams(this.addCredentialsToParams(params)));
}
buildAuthHeader() {
if (this.clientAuthMethod === NbOAuth2ClientAuthMethod.BASIC) {
if (this.getOption('clientId') && this.getOption('clientSecret')) {
return new HttpHeaders({
Authorization: 'Basic ' + btoa(this.getOption('clientId') + ':' + this.getOption('clientSecret')),
});
}
else {
throw Error('For basic client authentication method, please provide both clientId & clientSecret.');
}
}
return undefined;
}
getHeaders() {
let headers = super.getHeaders();
headers = headers.append('Content-Type', 'application/x-www-form-urlencoded');
const authHeaders = this.buildAuthHeader();
if (authHeaders === undefined) {
return headers;
}
for (const headerKey of authHeaders.keys()) {
for (const headerValue of authHeaders.getAll(headerKey)) {
headers = headers.append(headerKey, headerValue);
}
}
return headers;
}
cleanParams(params) {
Object.entries(params).forEach(([key, val]) => !val && delete params[key]);
return params;
}
addCredentialsToParams(params) {
if (this.clientAuthMethod === NbOAuth2ClientAuthMethod.REQUEST_BODY) {
if (this.getOption('clientId') && this.getOption('clientSecret')) {
return {
...params,
client_id: this.getOption('clientId'),
client_secret: this.getOption('clientSecret'),
};
}
else {
throw Error('For request body client authentication method, please provide both clientId & clientSecret.');
}
}
return params;
}
handleResponseError(res) {
let errors = [];
if (res instanceof HttpErrorResponse) {
if (res.error.error_description) {
errors.push(res.error.error_description);
}
else {
errors = this.getOption('defaultErrors');
}
}
else if (res instanceof NbAuthIllegalTokenError) {
errors.push(res.message);
}
else {
errors.push('Something went wrong.');
}
return of(new NbAuthResult(false, res, this.getOption('redirect.failure'), errors, []));
}
buildRedirectUrl() {
const params = {
response_type: this.getOption('authorize.responseType'),
client_id: this.getOption('clientId'),
redirect_uri: this.getOption('authorize.redirectUri'),
scope: this.getOption('authorize.scope'),
state: this.getOption('authorize.state'),
...this.getOption('authorize.params'),
};
const endpoint = this.getActionEndpoint('authorize');
const query = this.urlEncodeParameters(this.cleanParams(params));
return `${endpoint}?${query}`;
}
parseHashAsQueryParams(hash) {
return hash
? hash.split('&').reduce((acc, part) => {
const item = part.split('=');
acc[item[0]] = decodeURIComponent(item[1]);
return acc;
}, {})
: {};
}
urlEncodeParameters(params) {
return Object.keys(params)
.map((k) => {
return `${encodeURIComponent(k)}=${encodeURIComponent(params[k])}`;
})
.join('&');
}
createRefreshedToken(res, existingToken, requireValidToken) {
const refreshedToken = this.createToken(res, requireValidToken);
if (!refreshedToken.getRefreshToken() && existingToken.getRefreshToken()) {
refreshedToken.setRefreshToken(existingToken.getRefreshToken());
}
return refreshedToken;
}
register(data) {
throw new Error('`register` is not supported by `NbOAuth2AuthStrategy`, use `authenticate`.');
}
requestPassword(data) {
throw new Error('`requestPassword` is not supported by `NbOAuth2AuthStrategy`, use `authenticate`.');
}
resetPassword(data = {}) {
throw new Error('`resetPassword` is not supported by `NbOAuth2AuthStrategy`, use `authenticate`.');
}
logout() {
return of(new NbAuthResult(true));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: NbOAuth2AuthStrategy,