(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('rxjs/operators'), require('@bravobit/bb-foundation/storage'), require('@bravobit/bb-foundation/api'), require('rxjs'), require('@angular/router'), require('@angular/common/http')) :
    typeof define === 'function' && define.amd ? define('@bravobit/bb-foundation/auth', ['exports', '@angular/core', 'rxjs/operators', '@bravobit/bb-foundation/storage', '@bravobit/bb-foundation/api', 'rxjs', '@angular/router', '@angular/common/http'], factory) :
    (global = global || self, factory((global.bravobit = global.bravobit || {}, global.bravobit['bb-foundation'] = global.bravobit['bb-foundation'] || {}, global.bravobit['bb-foundation'].auth = {}), global.ng.core, global.rxjs.operators, global.bravobit['bb-foundation'].storage, global.bravobit['bb-foundation'].api, global.rxjs, global.ng.router, global.ng.common.http));
}(this, (function (exports, core, operators, storage, api, rxjs, router, http) { 'use strict';

    /*! *****************************************************************************
    Copyright (c) Microsoft Corporation. All rights reserved.
    Licensed under the Apache License, Version 2.0 (the "License"); you may not use
    this file except in compliance with the License. You may obtain a copy of the
    License at http://www.apache.org/licenses/LICENSE-2.0

    THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
    WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
    MERCHANTABLITY OR NON-INFRINGEMENT.

    See the Apache Version 2.0 License for specific language governing permissions
    and limitations under the License.
    ***************************************************************************** */
    /* global Reflect, Promise */

    var extendStatics = function(d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };

    function __extends(d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    }

    var __assign = function() {
        __assign = Object.assign || function __assign(t) {
            for (var s, i = 1, n = arguments.length; i < n; i++) {
                s = arguments[i];
                for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
            }
            return t;
        };
        return __assign.apply(this, arguments);
    };

    function __rest(s, e) {
        var t = {};
        for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
            t[p] = s[p];
        if (s != null && typeof Object.getOwnPropertySymbols === "function")
            for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
                if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                    t[p[i]] = s[p[i]];
            }
        return t;
    }

    function __decorate(decorators, target, key, desc) {
        var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
        if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
        else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
        return c > 3 && r && Object.defineProperty(target, key, r), r;
    }

    function __param(paramIndex, decorator) {
        return function (target, key) { decorator(target, key, paramIndex); }
    }

    function __metadata(metadataKey, metadataValue) {
        if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
    }

    function __awaiter(thisArg, _arguments, P, generator) {
        return new (P || (P = Promise))(function (resolve, reject) {
            function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
            function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
            function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
            step((generator = generator.apply(thisArg, _arguments || [])).next());
        });
    }

    function __generator(thisArg, body) {
        var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
        return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
        function verb(n) { return function (v) { return step([n, v]); }; }
        function step(op) {
            if (f) throw new TypeError("Generator is already executing.");
            while (_) try {
                if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
                if (y = 0, t) op = [op[0] & 2, t.value];
                switch (op[0]) {
                    case 0: case 1: t = op; break;
                    case 4: _.label++; return { value: op[1], done: false };
                    case 5: _.label++; y = op[1]; op = [0]; continue;
                    case 7: op = _.ops.pop(); _.trys.pop(); continue;
                    default:
                        if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                        if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                        if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                        if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                        if (t[2]) _.ops.pop();
                        _.trys.pop(); continue;
                }
                op = body.call(thisArg, _);
            } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
            if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
        }
    }

    function __exportStar(m, exports) {
        for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
    }

    function __values(o) {
        var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0;
        if (m) return m.call(o);
        return {
            next: function () {
                if (o && i >= o.length) o = void 0;
                return { value: o && o[i++], done: !o };
            }
        };
    }

    function __read(o, n) {
        var m = typeof Symbol === "function" && o[Symbol.iterator];
        if (!m) return o;
        var i = m.call(o), r, ar = [], e;
        try {
            while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
        }
        catch (error) { e = { error: error }; }
        finally {
            try {
                if (r && !r.done && (m = i["return"])) m.call(i);
            }
            finally { if (e) throw e.error; }
        }
        return ar;
    }

    function __spread() {
        for (var ar = [], i = 0; i < arguments.length; i++)
            ar = ar.concat(__read(arguments[i]));
        return ar;
    }

    function __spreadArrays() {
        for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
        for (var r = Array(s), k = 0, i = 0; i < il; i++)
            for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
                r[k] = a[j];
        return r;
    };

    function __await(v) {
        return this instanceof __await ? (this.v = v, this) : new __await(v);
    }

    function __asyncGenerator(thisArg, _arguments, generator) {
        if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
        var g = generator.apply(thisArg, _arguments || []), i, q = [];
        return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
        function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
        function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
        function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
        function fulfill(value) { resume("next", value); }
        function reject(value) { resume("throw", value); }
        function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
    }

    function __asyncDelegator(o) {
        var i, p;
        return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i;
        function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; }
    }

    function __asyncValues(o) {
        if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
        var m = o[Symbol.asyncIterator], i;
        return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
        function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
        function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
    }

    function __makeTemplateObject(cooked, raw) {
        if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
        return cooked;
    };

    function __importStar(mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
        result.default = mod;
        return result;
    }

    function __importDefault(mod) {
        return (mod && mod.__esModule) ? mod : { default: mod };
    }

    var AuthConfig = /** @class */ (function () {
        function AuthConfig() {
        }
        return AuthConfig;
    }());

    var JwtHelper = /** @class */ (function () {
        function JwtHelper() {
            var _this = this;
            this.baseDecodeUnicode = function (value) {
                return decodeURIComponent(atob(value).replace(/(.)/g, function (m, p) {
                    var code = p.charCodeAt(0).toString(16).toUpperCase();
                    if (code.length < 2) {
                        code = '0' + code;
                    }
                    return '%' + code;
                }));
            };
            this.parse = function (data) {
                return {
                    id: data['iss'] || null,
                    type: data['typ'] || null,
                    audience: data['aud'] || null,
                    issuer: data['iss'] || null,
                    subject: data['sub'] || null,
                    role: data['role'] || null,
                    notValidBefore: _this.parseDate(data['nbf']),
                    expiresAt: _this.parseDate(data['exp']),
                    issuedAt: _this.parseDate(data['iat'])
                };
            };
            this.parseDate = function (epochInSeconds) {
                if (!epochInSeconds || epochInSeconds <= 0) {
                    return null;
                }
                return new Date(epochInSeconds * 1000);
            };
        }
        JwtHelper.prototype.decode = function (token) {
            try {
                var json = JSON.parse(this.urlDecode(token.split('.')[1]));
                return this.parse(json);
            }
            catch (_a) {
                return null;
            }
        };
        JwtHelper.prototype.urlDecode = function (token) {
            var value = token || '';
            var output = value
                .replace(/-/g, '+')
                .replace(/_/g, '/');
            switch (output.length % 4) {
                case 0:
                    break;
                case 2:
                    output += '==';
                    break;
                case 3:
                    output += '=';
                    break;
                default:
                    throw 'Illegal base64url string!';
            }
            try {
                return this.baseDecodeUnicode(output);
            }
            catch (_a) {
                return atob(output);
            }
        };
        return JwtHelper;
    }());

    var AuthMapper = /** @class */ (function () {
        function AuthMapper() {
            // Routes.
            this.me = 'auth/me';
            this.register = 'auth/register';
            this.resendCode = 'auth/resend';
            this.logout = 'auth/logout';
            this.refresh = 'auth/refresh';
            this.requestPassword = 'auth/reset';
            this.resetPassword = 'auth/reset-password';
        }
        AuthMapper.prototype.role = function (data) {
            return (data && data.role) || null;
        };
        AuthMapper.prototype.toRegister = function (data) {
            // Retrieve the params.
            var token = data.token, refresh_token = data.refresh_token, user = data.user;
            // Map the data to the correct format.
            return { accessToken: token, refreshToken: refresh_token, user: user };
        };
        AuthMapper.prototype.toRefresh = function (data) {
            // Retrieve the params.
            var token = data.token, refresh_token = data.refresh_token;
            // Map the data to the correct format.
            return { accessToken: token, refreshToken: refresh_token };
        };
        AuthMapper = __decorate([
            core.Injectable()
        ], AuthMapper);
        return AuthMapper;
    }());

    var AuthVerifyProvider = /** @class */ (function () {
        function AuthVerifyProvider(_code, _verifyToken, _endpoint) {
            if (_endpoint === void 0) { _endpoint = 'auth/verify'; }
            this._code = _code;
            this._verifyToken = _verifyToken;
            this._endpoint = _endpoint;
        }
        AuthVerifyProvider.prototype.authenticate = function (api) {
            return __awaiter(this, void 0, void 0, function () {
                var _a, token, refresh_token, user;
                return __generator(this, function (_b) {
                    switch (_b.label) {
                        case 0: return [4 /*yield*/, api.post(this._endpoint, {
                                token: this._code,
                                verify_token: this._verifyToken
                            })];
                        case 1:
                            _a = _b.sent(), token = _a.token, refresh_token = _a.refresh_token, user = _a.user;
                            // Map the data to the correct format.
                            return [2 /*return*/, { accessToken: token, refreshToken: refresh_token, user: user }];
                    }
                });
            });
        };
        return AuthVerifyProvider;
    }());

    var AuthEmailProvider = /** @class */ (function () {
        function AuthEmailProvider(_email, _password, _endpoint) {
            if (_endpoint === void 0) { _endpoint = 'auth/login'; }
            this._email = _email;
            this._password = _password;
            this._endpoint = _endpoint;
        }
        AuthEmailProvider.prototype.authenticate = function (api) {
            return __awaiter(this, void 0, void 0, function () {
                var _a, token, refresh_token, user, provider, verify_token;
                return __generator(this, function (_b) {
                    switch (_b.label) {
                        case 0: return [4 /*yield*/, api.post(this._endpoint, {
                                email: this._email,
                                password: this._password
                            })];
                        case 1:
                            _a = _b.sent(), token = _a.token, refresh_token = _a.refresh_token, user = _a.user, provider = _a.provider, verify_token = _a.verify_token;
                            // Map the data to the correct format.
                            return [2 /*return*/, {
                                    accessToken: token,
                                    refreshToken: refresh_token,
                                    user: user,
                                    provider: provider,
                                    verifyToken: verify_token
                                }];
                    }
                });
            });
        };
        return AuthEmailProvider;
    }());

    var Auth = /** @class */ (function () {
        function Auth(_storage, _mapper, _injector, _api, _config) {
            this._storage = _storage;
            this._mapper = _mapper;
            this._injector = _injector;
            this._api = _api;
            this._config = _config;
            // Data.
            this._user$ = new rxjs.BehaviorSubject(null);
            // Validate the API was injected.
            if (!this._api) {
                throw new Error('The API is required for this module to work.');
            }
            // Set the storage keys based on the application id.
            var applicationId = this.getConfigString('applicationId') || 'angular';
            this._apiUserStorageKey = applicationId + "_auth-user";
            this._apiTokenStorageKey = applicationId + "_auth-token";
            this._apiRefreshTokenStorageKey = applicationId + "_auth-refresh-token";
            // Fetch the user from the storage.
            var user = this._storage.cookie.get(this._apiUserStorageKey);
            // Cache the user in memory.
            this._user$.next(user);
        }
        Auth.prototype.initialize = function () {
            var _this = this;
            return function () { return __awaiter(_this, void 0, void 0, function () {
                var shouldBootstrap, user;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            shouldBootstrap = this.getConfigBoolean('bootstrap', true);
                            // Check if the app should bootstrap
                            // the authentication.
                            if (!shouldBootstrap) {
                                return [2 /*return*/];
                            }
                            // Only retrieve from the server
                            // when an access token is saved.
                            if (!this.accessToken && !this.refreshToken) {
                                return [2 /*return*/];
                            }
                            return [4 /*yield*/, this.me().pipe(operators.first()).toPromise()];
                        case 1:
                            user = _a.sent();
                            // Save the user in the storage.
                            return [2 /*return*/, this.setUser(user)];
                    }
                });
            }); };
        };
        Object.defineProperty(Auth.prototype, "isAuthenticated", {
            get: function () {
                return !!this.payload;
            },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(Auth.prototype, "user", {
            get: function () {
                return this._user$.asObservable();
            },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(Auth.prototype, "accessToken", {
            get: function () {
                return this._storage.cookie.get(this._apiTokenStorageKey);
            },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(Auth.prototype, "refreshToken", {
            get: function () {
                return this._storage.cookie.get(this._apiRefreshTokenStorageKey);
            },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(Auth.prototype, "payload", {
            get: function () {
                return new JwtHelper().decode(this.accessToken);
            },
            enumerable: true,
            configurable: true
        });
        Auth.prototype.me = function () {
            return this._api.get(this._mapper.me);
        };
        Auth.prototype.signIn = function (provider, as) {
            return __awaiter(this, void 0, void 0, function () {
                var _a, accessToken, refreshToken, user, result, apiProvider, apiVerifyToken, availableProviders;
                return __generator(this, function (_b) {
                    switch (_b.label) {
                        case 0: return [4 /*yield*/, provider.authenticate(this._api)];
                        case 1:
                            _a = _b.sent(), accessToken = _a.accessToken, refreshToken = _a.refreshToken, user = _a.user, result = __rest(_a, ["accessToken", "refreshToken", "user"]);
                            // Check if the role matches.
                            if (as && !as.includes(this._mapper.role(user))) {
                                throw new Error('Invalid role.');
                            }
                            apiProvider = (result && result.provider) || null;
                            apiVerifyToken = (result && result.verifyToken) || null;
                            availableProviders = ['email', 'sms', 'totp'];
                            if (availableProviders.includes(apiProvider)) {
                                return [2 /*return*/, { user: user, provider: apiProvider, verifyToken: apiVerifyToken }];
                            }
                            // Set the tokens in storage.
                            this.setTokens(accessToken, refreshToken);
                            // Set the user in storage.
                            this.setUser(user);
                            // Return the user.
                            return [2 /*return*/, { user: user }];
                    }
                });
            });
        };
        Auth.prototype.signInWithEmail = function (email, password, as) {
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    return [2 /*return*/, this.signIn(new AuthEmailProvider(email, password), as)];
                });
            });
        };
        Auth.prototype.signInWithVerifyCode = function (code, verifyToken) {
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    return [2 /*return*/, this.signIn(new AuthVerifyProvider(code, verifyToken))];
                });
            });
        };
        Auth.prototype.resendVerifyCode = function (verifyToken) {
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0: return [4 /*yield*/, this._api.post(this._mapper.resendCode, {
                                verify_token: verifyToken
                            })];
                        case 1: return [2 /*return*/, _a.sent()];
                    }
                });
            });
        };
        Auth.prototype.register = function (data) {
            return __awaiter(this, void 0, void 0, function () {
                var result, _a, accessToken, refreshToken, user;
                return __generator(this, function (_b) {
                    switch (_b.label) {
                        case 0: return [4 /*yield*/, this._api.post(this._mapper.register, data)];
                        case 1:
                            result = _b.sent();
                            _a = this._mapper.toRegister(result), accessToken = _a.accessToken, refreshToken = _a.refreshToken, user = _a.user;
                            // Set the tokens in storage.
                            this.setTokens(accessToken, refreshToken);
                            // Set the user in storage.
                            this.setUser(user);
                            // Return the user.
                            return [2 /*return*/, user];
                    }
                });
            });
        };
        Auth.prototype.logout = function () {
            try {
                // Log the user out.
                this._api.get(this._mapper.logout, { headers: { Authorization: this.refreshToken } })
                    .toPromise().then(function (_) { return _; });
            }
            catch (_a) {
                // Do nothing because the tokens will be deleted anyways from the storage.
            }
            // Delete the tokens from the session.
            return this.clear();
        };
        Auth.prototype.refresh = function () {
            var _this = this;
            // If the refresh token does
            // not exist just return an observable of null.
            if (!this.refreshToken) {
                return rxjs.of(null);
            }
            // Perform the refresh call.
            var scheme = this.getConfigString('schema', 'Bearer');
            return this._api.get(this._mapper.refresh, {
                headers: {
                    'Authorization': scheme + " " + this.refreshToken,
                    'X-No-Authorization': 'yes'
                }
            }).pipe(operators.map(function (data) { return _this._mapper.toRefresh(data); }), operators.tap(function (_a) {
                var accessToken = _a.accessToken, refreshToken = _a.refreshToken;
                return _this.setTokens(accessToken, refreshToken);
            }), operators.map(function (_a) {
                var accessToken = _a.accessToken;
                return accessToken;
            }));
        };
        Auth.prototype.requestPassword = function (email) {
            return __awaiter(this, void 0, void 0, function () {
                var error_1;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            _a.trys.push([0, 2, , 3]);
                            return [4 /*yield*/, this._api.post(this._mapper.requestPassword, { email: email })];
                        case 1: return [2 /*return*/, _a.sent()];
                        case 2:
                            error_1 = _a.sent();
                            throw error_1;
                        case 3: return [2 /*return*/];
                    }
                });
            });
        };
        Auth.prototype.resetPassword = function (token, newPassword) {
            return __awaiter(this, void 0, void 0, function () {
                var error_2;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            _a.trys.push([0, 2, , 3]);
                            return [4 /*yield*/, this._api.post(this._mapper.resetPassword, { token: token, password: newPassword })];
                        case 1: return [2 /*return*/, _a.sent()];
                        case 2:
                            error_2 = _a.sent();
                            throw error_2;
                        case 3: return [2 /*return*/];
                    }
                });
            });
        };
        Auth.prototype.guard = function (type, redirectUrl) {
            var newUrl = null;
            // Get the correct new url.
            switch (type) {
                case 'authenticated':
                    newUrl = this.getConfigString('loggedInUrl', null);
                    break;
                case 'unauthenticated':
                    newUrl = this.getConfigString('redirectUrl', null);
                    break;
            }
            // Append a redirect url if the user is deemed
            // unauthenticated.
            if (type === 'unauthenticated') {
                var setRedirectOnFailedAuth = this.getConfigBoolean('setRedirectOnFailedAuth', true);
                if (setRedirectOnFailedAuth && redirectUrl && newUrl) {
                    newUrl += "?redirectUrl=" + redirectUrl;
                }
            }
            // Parse the url if it exists.
            if (this.router && newUrl) {
                return this.router.parseUrl(newUrl);
            }
            // Return false if the user is not allowed.
            return false;
        };
        Auth.prototype.setTokens = function (accessToken, refreshToken) {
            // Set the token if the token exists.
            if (accessToken) {
                var expiresAt = new JwtHelper().decode(accessToken).expiresAt;
                this._storage.cookie.set(this._apiTokenStorageKey, accessToken, {
                    expires: expiresAt
                });
            }
            // Set the refresh token if the refresh token exists.
            if (refreshToken) {
                var expiresAt = new JwtHelper().decode(refreshToken).expiresAt;
                this._storage.cookie.set(this._apiRefreshTokenStorageKey, refreshToken, {
                    expires: expiresAt
                });
            }
        };
        Auth.prototype.setUser = function (user) {
            // Validate the user exists.
            if (!user) {
                return;
            }
            // Push the next user.
            this._user$.next(user);
            // Make the user data valid for one year.
            var date = new Date();
            date.setFullYear(date.getFullYear() + 1);
            var timestamp = date.getTime();
            // Save the user in the storage.
            return this._storage.cookie.set(this._apiUserStorageKey, user, {
                expires: timestamp
            });
        };
        Auth.prototype.clear = function () {
            // Delete the items from the local storage.
            this._storage.cookie.remove(this._apiUserStorageKey);
            this._storage.cookie.remove(this._apiTokenStorageKey);
            this._storage.cookie.remove(this._apiRefreshTokenStorageKey);
            // Push an empty user.
            this._user$.next(null);
        };
        Object.defineProperty(Auth.prototype, "router", {
            get: function () {
                return this._injector.get(router.Router);
            },
            enumerable: true,
            configurable: true
        });
        Auth.prototype.getConfigBoolean = function (name, defaultValue) {
            if (defaultValue === void 0) { defaultValue = false; }
            return (this._config && this._config[name]) === undefined
                ? defaultValue
                : this._config[name];
        };
        Auth.prototype.getConfigString = function (name, defaultValue) {
            if (defaultValue === void 0) { defaultValue = null; }
            return (this._config && this._config[name]) === undefined
                ? defaultValue
                : this._config[name];
        };
        Auth.ctorParameters = function () { return [
            { type: storage.Storage },
            { type: AuthMapper },
            { type: core.Injector },
            { type: api.Api, decorators: [{ type: core.Optional }] },
            { type: AuthConfig, decorators: [{ type: core.Optional }] }
        ]; };
        Auth = __decorate([
            core.Injectable(),
            __param(3, core.Optional()),
            __param(4, core.Optional())
        ], Auth);
        return Auth;
    }());

    var BbAuthenticated = /** @class */ (function () {
        function BbAuthenticated(_auth, _template, _viewContainerRef) {
            this._auth = _auth;
            this._template = _template;
            this._viewContainerRef = _viewContainerRef;
        }
        BbAuthenticated.prototype.ngOnInit = function () {
            var _this = this;
            this._subscription = this._auth.user.pipe(operators.map(function (user) { return !!user; }), operators.distinctUntilChanged()).subscribe(function (isAuthenticated) {
                if (isAuthenticated) {
                    _this._viewContainerRef.createEmbeddedView(_this._template);
                }
                else {
                    _this._viewContainerRef.clear();
                }
            });
        };
        BbAuthenticated.prototype.ngOnDestroy = function () {
            this._subscription && this._subscription.unsubscribe();
        };
        BbAuthenticated.ctorParameters = function () { return [
            { type: Auth },
            { type: core.TemplateRef },
            { type: core.ViewContainerRef }
        ]; };
        BbAuthenticated = __decorate([
            core.Directive({
                selector: '[bbAuthenticated]'
            })
        ], BbAuthenticated);
        return BbAuthenticated;
    }());

    var BbRole = /** @class */ (function () {
        function BbRole(_auth, _template, _viewContainerRef) {
            this._auth = _auth;
            this._template = _template;
            this._viewContainerRef = _viewContainerRef;
            this.getPermissions = function (value) {
                return Array.isArray(value) ? value : [value];
            };
        }
        BbRole.prototype.ngOnInit = function () {
            var _this = this;
            this._subscription = this._auth.user.pipe(operators.map(function (user) { return (user && user.role) || null; }), operators.distinctUntilChanged()).subscribe(function (role) {
                var permissions = _this.getPermissions(_this.bbRole);
                if (permissions.includes(role)) {
                    _this._viewContainerRef.createEmbeddedView(_this._template);
                }
                else {
                    _this._viewContainerRef.clear();
                }
            });
        };
        BbRole.prototype.ngOnDestroy = function () {
            this._subscription && this._subscription.unsubscribe();
        };
        BbRole.ctorParameters = function () { return [
            { type: Auth },
            { type: core.TemplateRef },
            { type: core.ViewContainerRef }
        ]; };
        __decorate([
            core.Input()
        ], BbRole.prototype, "bbRole", void 0);
        BbRole = __decorate([
            core.Directive({
                selector: '[bbRole]'
            })
        ], BbRole);
        return BbRole;
    }());

    var BbAnonymousGuard = /** @class */ (function () {
        function BbAnonymousGuard(_auth) {
            this._auth = _auth;
        }
        BbAnonymousGuard.prototype.canActivate = function (route, state) {
            return __awaiter(this, void 0, void 0, function () {
                var isAuthenticated;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0: return [4 /*yield*/, this._auth.user.pipe(operators.map(function (user) { return !!user; }), operators.first()).toPromise()];
                        case 1:
                            isAuthenticated = _a.sent();
                            // If the user is not authenticated it can
                            // access the anonymous page.
                            if (!isAuthenticated) {
                                return [2 /*return*/, true];
                            }
                            // Return the user back to the authenticated page.
                            return [2 /*return*/, this._auth.guard('authenticated', state.url)];
                    }
                });
            });
        };
        BbAnonymousGuard.prototype.canActivateChild = function (childRoute, state) {
            return this.canActivate(childRoute, state);
        };
        BbAnonymousGuard.ctorParameters = function () { return [
            { type: Auth }
        ]; };
        BbAnonymousGuard.ɵprov = core["ɵɵdefineInjectable"]({ factory: function BbAnonymousGuard_Factory() { return new BbAnonymousGuard(core["ɵɵinject"](Auth)); }, token: BbAnonymousGuard, providedIn: "root" });
        BbAnonymousGuard = __decorate([
            core.Injectable({
                providedIn: 'root'
            })
        ], BbAnonymousGuard);
        return BbAnonymousGuard;
    }());

    var BbAuthenticatedGuard = /** @class */ (function () {
        function BbAuthenticatedGuard(_auth) {
            this._auth = _auth;
        }
        BbAuthenticatedGuard.prototype.canActivate = function (route, state) {
            return __awaiter(this, void 0, void 0, function () {
                var isAuthenticated;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0: return [4 /*yield*/, this._auth.user.pipe(operators.map(function (user) { return !!user; }), operators.first()).toPromise()];
                        case 1:
                            isAuthenticated = _a.sent();
                            // If the user is authenticated it can
                            // access the authenticated page.
                            if (isAuthenticated) {
                                return [2 /*return*/, true];
                            }
                            // The user is not authorized to see that
                            // page so un-authorize him.
                            return [2 /*return*/, this._auth.guard('unauthenticated', state.url)];
                    }
                });
            });
        };
        BbAuthenticatedGuard.prototype.canActivateChild = function (childRoute, state) {
            return this.canActivate(childRoute, state);
        };
        BbAuthenticatedGuard.ctorParameters = function () { return [
            { type: Auth }
        ]; };
        BbAuthenticatedGuard.ɵprov = core["ɵɵdefineInjectable"]({ factory: function BbAuthenticatedGuard_Factory() { return new BbAuthenticatedGuard(core["ɵɵinject"](Auth)); }, token: BbAuthenticatedGuard, providedIn: "root" });
        BbAuthenticatedGuard = __decorate([
            core.Injectable({
                providedIn: 'root'
            })
        ], BbAuthenticatedGuard);
        return BbAuthenticatedGuard;
    }());

    var AuthInterceptor = /** @class */ (function () {
        function AuthInterceptor(_auth, _router, _config) {
            var _this = this;
            this._auth = _auth;
            this._router = _router;
            this._config = _config;
            // Data.
            this.isRefreshing = false;
            this.token$ = new rxjs.BehaviorSubject(null);
            this.getToken = function (request) {
                // Get the token based on header.
                if (request.headers.has('Authorization')) {
                    return request.headers.get('Authorization');
                }
                // Return the default access token.
                return _this._auth.accessToken;
            };
            this.composeRequest = function (request, token) {
                if (token === void 0) { token = null; }
                // If the token is not set, delete the authorization header.
                if (!token) {
                    return request.clone({ headers: request.headers.delete('Authorization') });
                }
                // Get the auth scheme from the config,
                // if the scheme was not set use bearer.
                var authScheme = (_this._config && _this._config.scheme) || 'Bearer';
                // Clone and add the authorization header.
                return request.clone({
                    setHeaders: { Authorization: authScheme + " " + token }
                });
            };
        }
        AuthInterceptor.prototype.intercept = function (request, next) {
            var _this = this;
            // Just return when the user is performing an refresh request.
            if (request.headers.has('X-No-Authorization')) {
                var newRequest_1 = request.clone({ headers: request.headers.delete('X-No-Authorization') });
                return next.handle(newRequest_1);
            }
            // Compose the new request.
            var newRequest = this.composeRequest(request, this.getToken(request));
            // Handle all errors.
            return next.handle(newRequest).pipe(operators.catchError(function (error) {
                // Create a custom error.
                var apiError = new api.ApiError(error.status, error.error);
                // Check if it is an error response.
                if (!(error instanceof http.HttpErrorResponse)) {
                    return rxjs.throwError(apiError);
                }
                // Handle a 401 error.
                if (error.status === 401) {
                    return _this.handle401Error(request, next);
                }
                // Just re-throw the error.
                return rxjs.throwError(apiError);
            }));
        };
        AuthInterceptor.prototype.handle401Error = function (request, next) {
            var _this = this;
            // If already refreshing wait for the token to complete.
            if (this.isRefreshing) {
                return this.token$.pipe(operators.filter(function (token) { return token !== null; }), operators.take(1), operators.switchMap(function (token) { return next.handle(_this.composeRequest(request, token)); }));
            }
            // Set the refreshing to true.
            this.isRefreshing = true;
            this.token$.next(null);
            return this._auth.refresh().pipe(operators.switchMap(function (newToken) {
                if (!newToken) {
                    return rxjs.throwError('No refresh token was available.');
                }
                _this.token$.next(newToken);
                return next.handle(_this.composeRequest(request, newToken));
            }), operators.catchError(function (error) {
                return _this.logoutUser(error);
            }), operators.finalize(function () {
                _this.isRefreshing = false;
            }));
        };
        AuthInterceptor.prototype.logoutUser = function (error) {
            // Delete the tokens from the session.
            this._auth.clear();
            // Compose the route url.
            var routeUrl = this._config && this._config.redirectUrl;
            // Route back if the user provided a redirect url.
            if (this._router && routeUrl) {
                this._router.navigate([routeUrl]).then(function (_) { return _; });
            }
            // Throw the error.
            return rxjs.throwError(error || 'Invalid refresh token; user was logged out.');
        };
        AuthInterceptor.ctorParameters = function () { return [
            { type: Auth },
            { type: router.Router, decorators: [{ type: core.Optional }] },
            { type: AuthConfig, decorators: [{ type: core.Optional }] }
        ]; };
        AuthInterceptor = __decorate([
            core.Injectable(),
            __param(1, core.Optional()),
            __param(2, core.Optional())
        ], AuthInterceptor);
        return AuthInterceptor;
    }());

    var DECLARATIONS_EXPORTS = [
        BbAuthenticated,
        BbRole
    ];
    var AuthModule = /** @class */ (function () {
        function AuthModule() {
        }
        AuthModule_1 = AuthModule;
        AuthModule.forRoot = function (config) {
            return {
                ngModule: AuthModule_1,
                providers: [
                    Auth,
                    { provide: AuthConfig, useValue: config },
                    { provide: http.HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
                    { useFactory: initializeAuth, provide: core.APP_INITIALIZER, deps: [Auth], multi: true }
                ]
            };
        };
        var AuthModule_1;
        AuthModule = AuthModule_1 = __decorate([
            core.NgModule({
                imports: [http.HttpClientModule],
                declarations: __spread(DECLARATIONS_EXPORTS),
                exports: __spread(DECLARATIONS_EXPORTS),
                providers: [
                    { provide: AuthMapper, useClass: AuthMapper }
                ]
            })
        ], AuthModule);
        return AuthModule;
    }());
    function initializeAuth(auth) {
        return auth.initialize();
    }

    exports.Auth = Auth;
    exports.AuthConfig = AuthConfig;
    exports.AuthEmailProvider = AuthEmailProvider;
    exports.AuthMapper = AuthMapper;
    exports.AuthModule = AuthModule;
    exports.AuthVerifyProvider = AuthVerifyProvider;
    exports.BbAnonymousGuard = BbAnonymousGuard;
    exports.BbAuthenticated = BbAuthenticated;
    exports.BbAuthenticatedGuard = BbAuthenticatedGuard;
    exports.BbRole = BbRole;
    exports.JwtHelper = JwtHelper;
    exports.initializeAuth = initializeAuth;
    exports.ɵa = AuthInterceptor;

    Object.defineProperty(exports, '__esModule', { value: true });

})));
//# sourceMappingURL=bravobit-bb-foundation-auth.umd.js.map
