@okta/okta-angular
Version:
Angular support for Okta
473 lines (459 loc) • 23.9 kB
JavaScript
import * as i0 from '@angular/core';
import { InjectionToken, Injectable, Optional, Inject, Component, VERSION, Directive, Input, NgModule } from '@angular/core';
import { Router, NavigationStart } from '@angular/router';
import { Location } from '@angular/common';
import * as i2 from '@okta/okta-auth-js';
import { AuthSdkError, toRelativeUrl } from '@okta/okta-auth-js';
import { filter, mergeMap, switchMap, takeUntil } from 'rxjs/operators';
import { BehaviorSubject, ReplaySubject, Subject } from 'rxjs';
import { compare } from 'compare-versions';
/*
* Copyright (c) 2017-Present, Okta, Inc. and/or its affiliates. All rights reserved.
* The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and limitations under the License.
*/
const OKTA_CONFIG = new InjectionToken('okta.config.angular');
const OKTA_AUTH = new InjectionToken('okta.auth');
class OktaAuthConfigService {
constructor(config) {
if (config) {
this.config = config;
}
}
getConfig() {
return this.config;
}
setConfig(config) {
this.config = config;
}
}
OktaAuthConfigService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: OktaAuthConfigService, deps: [{ token: OKTA_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
OktaAuthConfigService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: OktaAuthConfigService, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: OktaAuthConfigService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [OKTA_CONFIG]
}] }]; } });
var __awaiter$3 = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
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) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
class OktaCallbackComponent {
constructor(configService, oktaAuth, injector) {
this.configService = configService;
this.oktaAuth = oktaAuth;
this.injector = injector;
}
ngOnInit() {
return __awaiter$3(this, void 0, void 0, function* () {
const config = this.configService.getConfig();
if (!config) {
throw new Error('Okta config is not provided');
}
try {
// Parse code or tokens from the URL, store tokens in the TokenManager, and redirect back to the originalUri
yield this.oktaAuth.handleLoginRedirect();
}
catch (e) {
// Callback from social IDP. Show custom login page to continue.
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore Supports auth-js v5 & v6-7
const isInteractionRequiredError = this.oktaAuth.isInteractionRequiredError || this.oktaAuth.idx.isInteractionRequiredError;
if (isInteractionRequiredError(e) && this.injector) {
const { onAuthResume, onAuthRequired } = config;
const callbackFn = onAuthResume || onAuthRequired;
if (callbackFn) {
callbackFn(this.oktaAuth, this.injector);
return;
}
}
this.error = e.toString();
}
});
}
}
OktaCallbackComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: OktaCallbackComponent, deps: [{ token: OktaAuthConfigService }, { token: OKTA_AUTH }, { token: i0.Injector, optional: true }], target: i0.ɵɵFactoryTarget.Component });
OktaCallbackComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.17", type: OktaCallbackComponent, selector: "ng-component", ngImport: i0, template: `<div>{{error}}</div>`, isInline: true });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: OktaCallbackComponent, decorators: [{
type: Component,
args: [{
template: `<div>{{error}}</div>`
}]
}], ctorParameters: function () { return [{ type: OktaAuthConfigService }, { type: i2.OktaAuth, decorators: [{
type: Inject,
args: [OKTA_AUTH]
}] }, { type: i0.Injector, decorators: [{
type: Optional
}] }]; } });
var __awaiter$2 = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
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) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
class OktaAuthGuard {
constructor(oktaAuth, injector, configService) {
this.oktaAuth = oktaAuth;
this.injector = injector;
this.configService = configService;
this.updateAuthStateListener = (authState) => __awaiter$2(this, void 0, void 0, function* () {
const isAuthenticated = yield this.isAuthenticated(this.routeData, authState);
if (!isAuthenticated) {
this.handleLogin(this.state.url, this.routeData);
}
});
const config = this.configService.getConfig();
if (!config) {
throw new Error('Okta config is not provided');
}
this.onAuthRequired = config.onAuthRequired;
// Unsubscribe updateAuthStateListener when route change
const router = injector.get(Router);
router.events.pipe(filter((e) => e instanceof NavigationStart && this.state && this.state.url !== e.url)).subscribe(() => {
this.oktaAuth.authStateManager.unsubscribe(this.updateAuthStateListener);
});
}
canLoad(route) {
var _a;
return __awaiter$2(this, void 0, void 0, function* () {
this.onAuthRequired = ((_a = route.data) === null || _a === void 0 ? void 0 : _a.onAuthRequired) || this.onAuthRequired;
const isAuthenticated = yield this.isAuthenticated(route.data);
if (isAuthenticated) {
return true;
}
const router = this.injector.get(Router);
const nav = router.getCurrentNavigation();
const originalUri = nav ? nav.extractedUrl.toString() : undefined;
yield this.handleLogin(originalUri, route.data);
return false;
});
}
/**
* Gateway for protected route. Returns true if there is a valid idToken,
* otherwise it will cache the route and start the login flow.
* @param route
* @param state
*/
canActivate(route, state) {
return __awaiter$2(this, void 0, void 0, function* () {
// Track states for current route
this.state = state;
this.routeData = route.data;
this.onAuthRequired = route.data && route.data.onAuthRequired || this.onAuthRequired;
// Protect the route after accessing
this.oktaAuth.authStateManager.subscribe(this.updateAuthStateListener);
const isAuthenticated = yield this.isAuthenticated(route.data);
if (isAuthenticated) {
return true;
}
yield this.handleLogin(state.url, route.data);
return false;
});
}
canActivateChild(route, state) {
return __awaiter$2(this, void 0, void 0, function* () {
return this.canActivate(route, state);
});
}
isAuthenticated(routeData, authState) {
var _a, _b, _c;
return __awaiter$2(this, void 0, void 0, function* () {
const isAuthenticated = authState ? authState === null || authState === void 0 ? void 0 : authState.isAuthenticated : yield this.oktaAuth.isAuthenticated();
let res = isAuthenticated;
if ((_a = routeData === null || routeData === void 0 ? void 0 : routeData.okta) === null || _a === void 0 ? void 0 : _a.acrValues) {
if (!authState) {
authState = this.oktaAuth.authStateManager.getAuthState();
}
res = ((_b = authState === null || authState === void 0 ? void 0 : authState.idToken) === null || _b === void 0 ? void 0 : _b.claims.acr) === ((_c = routeData === null || routeData === void 0 ? void 0 : routeData.okta) === null || _c === void 0 ? void 0 : _c.acrValues);
}
return res;
});
}
handleLogin(originalUri, routeData) {
var _a;
return __awaiter$2(this, void 0, void 0, function* () {
// Store the current path
if (originalUri) {
this.oktaAuth.setOriginalUri(originalUri);
}
const options = {};
if ((_a = routeData === null || routeData === void 0 ? void 0 : routeData.okta) === null || _a === void 0 ? void 0 : _a.acrValues) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore Supports auth-js >= 7.1.0
options.acrValues = routeData.okta.acrValues;
}
if (this.onAuthRequired) {
this.onAuthRequired(this.oktaAuth, this.injector, options);
}
else {
this.oktaAuth.signInWithRedirect(options);
}
});
}
}
OktaAuthGuard.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: OktaAuthGuard, deps: [{ token: OKTA_AUTH }, { token: i0.Injector }, { token: OktaAuthConfigService }], target: i0.ɵɵFactoryTarget.Injectable });
OktaAuthGuard.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: OktaAuthGuard });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: OktaAuthGuard, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: i2.OktaAuth, decorators: [{
type: Inject,
args: [OKTA_AUTH]
}] }, { type: i0.Injector }, { type: OktaAuthConfigService }]; } });
var __awaiter$1 = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
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) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
const defaultAuthState = {
isAuthenticated: false
};
class OktaAuthStateService {
constructor(oktaAuth) {
this.oktaAuth = oktaAuth;
this._authState = new BehaviorSubject(defaultAuthState);
// only expose readonly property
this.authState$ = this._authState.asObservable();
this.updateAuthState = this.updateAuthState.bind(this);
// set initial authState
const initialAuthState = this.oktaAuth.authStateManager.getAuthState() || defaultAuthState;
this._authState.next(initialAuthState);
// subscribe to future changes
this.oktaAuth.authStateManager.subscribe(this.updateAuthState);
}
ngOnDestroy() {
this.oktaAuth.authStateManager.unsubscribe(this.updateAuthState);
}
// Observes as true when any group input can match groups from user claims
hasAnyGroups(groups) {
return this.authState$.pipe(mergeMap(({ isAuthenticated, idToken }) => __awaiter$1(this, void 0, void 0, function* () {
// return false when not authenticated or openid is not in scopes
if (!isAuthenticated || !idToken) {
return false;
}
// transform inputs to consistent object format
if (typeof groups === 'string') {
groups = { groups: [groups] };
}
if (Array.isArray(groups)) {
groups = { groups };
}
const key = Object.keys(groups)[0];
const value = groups[key];
// groups or custom claims is available in idToken
if (idToken.claims[key]) {
return value.some((authority) => idToken.claims[key].includes(authority));
}
// try /userinfo endpoint when thin idToken (no groups claim) is returned
// https://developer.okta.com/docs/concepts/api-access-management/#tokens-and-scopes
const userInfo = yield this.oktaAuth.getUser();
if (!userInfo[key]) {
return false;
}
return value.some((authority) => userInfo[key].includes(authority));
})));
}
updateAuthState(authState) {
this._authState.next(authState);
}
}
OktaAuthStateService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: OktaAuthStateService, deps: [{ token: OKTA_AUTH }], target: i0.ɵɵFactoryTarget.Injectable });
OktaAuthStateService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: OktaAuthStateService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: OktaAuthStateService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: i2.OktaAuth, decorators: [{
type: Inject,
args: [OKTA_AUTH]
}] }]; } });
var packageInfo = {
'name': '@okta/okta-angular',
'version': '6.5.1',
'authJSMinSupportedVersion': '5.3.1'
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
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) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
class OktaAuthFactoryService {
static setupOktaAuth(oktaAuth, router, location) {
const isAuthJsSupported = oktaAuth._oktaUserAgent && compare(oktaAuth._oktaUserAgent.getVersion(), packageInfo.authJSMinSupportedVersion, '>=');
if (!isAuthJsSupported) {
throw new AuthSdkError(`Passed in oktaAuth is not compatible with the SDK, minimum supported okta-auth-js version is ${packageInfo.authJSMinSupportedVersion}.`);
}
// Add Okta UA
oktaAuth._oktaUserAgent.addEnvironment(`${packageInfo.name}/${packageInfo.version}`);
oktaAuth._oktaUserAgent.addEnvironment(`Angular/${VERSION.full}`);
// Provide a default implementation of `restoreOriginalUri`
if (!oktaAuth.options.restoreOriginalUri && router && location) {
oktaAuth.options.restoreOriginalUri = (_, originalUri) => __awaiter(this, void 0, void 0, function* () {
const baseUrl = window.location.origin + location.prepareExternalUrl('');
const routePath = toRelativeUrl(originalUri || '/', baseUrl);
router.navigateByUrl(routePath);
});
}
// Start services
oktaAuth.start();
}
static createOktaAuth(configService, router, location) {
const config = configService.getConfig();
if (!config) {
throw new Error('Okta config is not provided');
}
const { oktaAuth } = config;
if (!oktaAuth) {
throw new Error('Okta config should contain oktaAuth');
}
OktaAuthFactoryService.setupOktaAuth(oktaAuth, router, location);
return oktaAuth;
}
}
class OktaHasAnyGroupDirective {
constructor(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
templateRef, viewContainer, authStateService) {
this.templateRef = templateRef;
this.viewContainer = viewContainer;
this.authStateService = authStateService;
this.groupsSub$ = new ReplaySubject();
this.destroySub$ = new Subject();
}
ngOnInit() {
this.groupsSub$.pipe(filter(groups => !!groups), switchMap(groups => this.authStateService.hasAnyGroups(groups)), takeUntil(this.destroySub$)).subscribe(isAuthorized => {
this.viewContainer.clear();
if (isAuthorized) {
this.viewContainer.createEmbeddedView(this.templateRef);
}
});
}
ngOnChanges(changes) {
if (changes['oktaHasAnyGroup'].currentValue !== changes['oktaHasAnyGroup'].previousValue) {
this.groupsSub$.next(changes['oktaHasAnyGroup'].currentValue);
}
}
ngOnDestroy() {
this.destroySub$.next();
this.destroySub$.complete();
}
}
OktaHasAnyGroupDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: OktaHasAnyGroupDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }, { token: OktaAuthStateService }], target: i0.ɵɵFactoryTarget.Directive });
OktaHasAnyGroupDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "12.2.17", type: OktaHasAnyGroupDirective, selector: "[oktaHasAnyGroup]", inputs: { oktaHasAnyGroup: "oktaHasAnyGroup" }, usesOnChanges: true, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: OktaHasAnyGroupDirective, decorators: [{
type: Directive,
args: [{ selector: '[oktaHasAnyGroup]' }]
}], ctorParameters: function () { return [{ type: i0.TemplateRef }, { type: i0.ViewContainerRef }, { type: OktaAuthStateService }]; }, propDecorators: { oktaHasAnyGroup: [{
type: Input
}] } });
/*
* Copyright (c) 2017-Present, Okta, Inc. and/or its affiliates. All rights reserved.
* The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and limitations under the License.
*/
class OktaAuthModule {
static forRoot(config) {
return {
ngModule: OktaAuthModule,
providers: [
{ provide: OKTA_CONFIG, useValue: config },
]
};
}
}
OktaAuthModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: OktaAuthModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
OktaAuthModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: OktaAuthModule, declarations: [OktaCallbackComponent,
OktaHasAnyGroupDirective], exports: [OktaCallbackComponent,
OktaHasAnyGroupDirective] });
OktaAuthModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: OktaAuthModule, providers: [
OktaAuthConfigService,
OktaAuthStateService,
OktaAuthGuard,
{
provide: OKTA_AUTH,
useFactory: OktaAuthFactoryService.createOktaAuth,
deps: [
OktaAuthConfigService,
[new Optional(), Router],
[new Optional(), Location]
]
},
] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: OktaAuthModule, decorators: [{
type: NgModule,
args: [{
declarations: [
OktaCallbackComponent,
OktaHasAnyGroupDirective,
],
exports: [
OktaCallbackComponent,
OktaHasAnyGroupDirective,
],
providers: [
OktaAuthConfigService,
OktaAuthStateService,
OktaAuthGuard,
{
provide: OKTA_AUTH,
useFactory: OktaAuthFactoryService.createOktaAuth,
deps: [
OktaAuthConfigService,
[new Optional(), Router],
[new Optional(), Location]
]
},
]
}]
}] });
/*
* Copyright (c) 2017-Present, Okta, Inc. and/or its affiliates. All rights reserved.
* The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and limitations under the License.
*/
/**
* Entry point for all public APIs of the package.
*/
// This file only reexports content of the `src` folder. Keep it that way.
/**
* Generated bundle index. Do not edit.
*/
export { OKTA_AUTH, OKTA_CONFIG, OktaAuthConfigService, OktaAuthGuard, OktaAuthModule, OktaAuthStateService, OktaCallbackComponent, OktaHasAnyGroupDirective };
//# sourceMappingURL=okta-okta-angular.js.map