angular-oauth2-oidc
Advanced tools
Comparing version 2.0.15 to 2.1.0
@@ -1,1 +0,1 @@ | ||
{"__symbolic":"module","version":3,"metadata":{"OAuthModule":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"NgModule"},"arguments":[{"imports":[{"__symbolic":"reference","module":"@angular/common","name":"CommonModule"}],"declarations":[],"exports":[]}]}],"members":{},"statics":{"forRoot":{"__symbolic":"function","parameters":[],"value":{"ngModule":{"__symbolic":"reference","name":"OAuthModule"},"providers":[{"__symbolic":"reference","name":"OAuthService"},{"__symbolic":"reference","name":"UrlHelperService"}]}}}},"OAuthService":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Injectable"}}],"members":{"__ctor__":[{"__symbolic":"constructor","parameters":[{"__symbolic":"reference","module":"@angular/http","name":"Http"},{"__symbolic":"reference","name":"UrlHelperService"}]}],"getKeyCount":[{"__symbolic":"method"}],"debug":[{"__symbolic":"method"}],"validateUrlFromDiscoveryDocument":[{"__symbolic":"method"}],"validateUrlForHttps":[{"__symbolic":"method"}],"validateUrlAgainstIssuer":[{"__symbolic":"method"}],"setupTimer":[{"__symbolic":"method"}],"setupAccessTokenTimer":[{"__symbolic":"method"}],"setupIdTokenTimer":[{"__symbolic":"method"}],"clearAccessTokenTimer":[{"__symbolic":"method"}],"clearIdTokenTimer":[{"__symbolic":"method"}],"calcTimeout":[{"__symbolic":"method"}],"setStorage":[{"__symbolic":"method"}],"loadDiscoveryDocument":[{"__symbolic":"method"}],"loadJwks":[{"__symbolic":"method"}],"validateDiscoveryDocument":[{"__symbolic":"method"}],"fetchTokenUsingPasswordFlowAndLoadUserProfile":[{"__symbolic":"method"}],"loadUserProfile":[{"__symbolic":"method"}],"fetchTokenUsingPasswordFlow":[{"__symbolic":"method"}],"refreshToken":[{"__symbolic":"method"}],"removeSilentRefreshEventListener":[{"__symbolic":"method"}],"setupSilentRefreshEventListener":[{"__symbolic":"method"}],"silentRefresh":[{"__symbolic":"method"}],"createLoginUrl":[{"__symbolic":"method"}],"initImplicitFlow":[{"__symbolic":"method"}],"callOnTokenReceivedIfExists":[{"__symbolic":"method"}],"storeAccessTokenResponse":[{"__symbolic":"method"}],"tryLogin":[{"__symbolic":"method"}],"validateNonceForAccessToken":[{"__symbolic":"method"}],"storeIdToken":[{"__symbolic":"method"}],"handleLoginError":[{"__symbolic":"method"}],"processIdToken":[{"__symbolic":"method"}],"getIdentityClaims":[{"__symbolic":"method"}],"getIdToken":[{"__symbolic":"method"}],"padBase64":[{"__symbolic":"method"}],"getAccessToken":[{"__symbolic":"method"}],"getAccessTokenExpiration":[{"__symbolic":"method"}],"getIdTokenExpiration":[{"__symbolic":"method"}],"hasValidAccessToken":[{"__symbolic":"method"}],"hasValidIdToken":[{"__symbolic":"method"}],"authorizationHeader":[{"__symbolic":"method"}],"logOut":[{"__symbolic":"method"}],"createAndSaveNonce":[{"__symbolic":"method"}],"createNonce":[{"__symbolic":"method"}],"checkAtHash":[{"__symbolic":"method"}],"checkSignature":[{"__symbolic":"method"}]}},"JwksValidationHandler":{"__symbolic":"class","extends":{"__symbolic":"reference","name":"AbstractValidationHandler"},"members":{"validateSignature":[{"__symbolic":"method"}],"alg2kty":[{"__symbolic":"method"}],"calcHash":[{"__symbolic":"method"}],"toByteArrayAsString":[{"__symbolic":"method"}]}},"NullValidationHandler":{"__symbolic":"class","members":{"validateSignature":[{"__symbolic":"method"}],"validateAtHash":[{"__symbolic":"method"}]}},"ValidationParams":{"__symbolic":"interface"},"ValidationHandler":{"__symbolic":"interface"},"AbstractValidationHandler":{"__symbolic":"class","members":{"validateSignature":[{"__symbolic":"method"}],"validateAtHash":[{"__symbolic":"method"}],"inferHashAlgorithm":[{"__symbolic":"method"}],"calcHash":[{"__symbolic":"method"}]}},"UrlHelperService":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Injectable"}}],"members":{"getHashFragmentParams":[{"__symbolic":"method"}],"parseQueryString":[{"__symbolic":"method"}]}}},"origins":{"OAuthModule":"./index","OAuthService":"./oauth-service","JwksValidationHandler":"./token-validation/jwks-validation-handler","NullValidationHandler":"./token-validation/null-validation-handler","ValidationParams":"./token-validation/validation-handler","ValidationHandler":"./token-validation/validation-handler","AbstractValidationHandler":"./token-validation/validation-handler","UrlHelperService":"./url-helper.service"},"importAs":"angular-oauth2-oidc"} | ||
{"__symbolic":"module","version":3,"metadata":{"OAuthModule":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"NgModule"},"arguments":[{"imports":[{"__symbolic":"reference","module":"@angular/common","name":"CommonModule"}],"declarations":[],"exports":[]}]}],"members":{},"statics":{"forRoot":{"__symbolic":"function","parameters":[],"value":{"ngModule":{"__symbolic":"reference","name":"OAuthModule"},"providers":[{"__symbolic":"reference","name":"OAuthService"},{"__symbolic":"reference","name":"UrlHelperService"}]}}}},"OAuthService":{"__symbolic":"class","extends":{"__symbolic":"reference","name":"AuthConfig"},"decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Injectable"}}],"members":{"__ctor__":[{"__symbolic":"constructor","parameterDecorators":[null,[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Optional"}}],[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Optional"}}],[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Optional"}}],null],"parameters":[{"__symbolic":"reference","module":"@angular/http","name":"Http"},{"__symbolic":"reference","name":"OAuthStorage"},{"__symbolic":"reference","name":"ValidationHandler"},{"__symbolic":"reference","name":"AuthConfig"},{"__symbolic":"reference","name":"UrlHelperService"}]}],"configure":[{"__symbolic":"method"}],"restartSessionChecksIfStillLoggedIn":[{"__symbolic":"method"}],"restartRefreshTimerIfStillLoggedIn":[{"__symbolic":"method"}],"setupSessionCheck":[{"__symbolic":"method"}],"setupAutomaticSilentRefresh":[{"__symbolic":"method"}],"loadDiscoveryDocumentAndTryLogin":[{"__symbolic":"method"}],"debug":[{"__symbolic":"method"}],"validateUrlFromDiscoveryDocument":[{"__symbolic":"method"}],"validateUrlForHttps":[{"__symbolic":"method"}],"validateUrlAgainstIssuer":[{"__symbolic":"method"}],"setupRefreshTimer":[{"__symbolic":"method"}],"setupAccessTokenTimer":[{"__symbolic":"method"}],"setupIdTokenTimer":[{"__symbolic":"method"}],"clearAccessTokenTimer":[{"__symbolic":"method"}],"clearIdTokenTimer":[{"__symbolic":"method"}],"calcTimeout":[{"__symbolic":"method"}],"setStorage":[{"__symbolic":"method"}],"loadDiscoveryDocument":[{"__symbolic":"method"}],"loadJwks":[{"__symbolic":"method"}],"validateDiscoveryDocument":[{"__symbolic":"method"}],"fetchTokenUsingPasswordFlowAndLoadUserProfile":[{"__symbolic":"method"}],"loadUserProfile":[{"__symbolic":"method"}],"fetchTokenUsingPasswordFlow":[{"__symbolic":"method"}],"refreshToken":[{"__symbolic":"method"}],"removeSilentRefreshEventListener":[{"__symbolic":"method"}],"setupSilentRefreshEventListener":[{"__symbolic":"method"}],"silentRefresh":[{"__symbolic":"method"}],"canPerformSessionCheck":[{"__symbolic":"method"}],"setupSessionCheckEventListener":[{"__symbolic":"method"}],"handleSessionUnchanged":[{"__symbolic":"method"}],"handleSessionChange":[{"__symbolic":"method"}],"waitForSilentRefreshAfterSessionChange":[{"__symbolic":"method"}],"handleSessionError":[{"__symbolic":"method"}],"removeSessionCheckEventListener":[{"__symbolic":"method"}],"initSessionCheck":[{"__symbolic":"method"}],"startSessionCheckTimer":[{"__symbolic":"method"}],"stopSessionCheckTimer":[{"__symbolic":"method"}],"checkSession":[{"__symbolic":"method"}],"createLoginUrl":[{"__symbolic":"method"}],"initImplicitFlow":[{"__symbolic":"method"}],"callOnTokenReceivedIfExists":[{"__symbolic":"method"}],"storeAccessTokenResponse":[{"__symbolic":"method"}],"tryLogin":[{"__symbolic":"method"}],"validateNonceForAccessToken":[{"__symbolic":"method"}],"storeIdToken":[{"__symbolic":"method"}],"storeSessionState":[{"__symbolic":"method"}],"getSessionState":[{"__symbolic":"method"}],"handleLoginError":[{"__symbolic":"method"}],"processIdToken":[{"__symbolic":"method"}],"getIdentityClaims":[{"__symbolic":"method"}],"getIdToken":[{"__symbolic":"method"}],"padBase64":[{"__symbolic":"method"}],"getAccessToken":[{"__symbolic":"method"}],"getAccessTokenExpiration":[{"__symbolic":"method"}],"getIdTokenExpiration":[{"__symbolic":"method"}],"hasValidAccessToken":[{"__symbolic":"method"}],"hasValidIdToken":[{"__symbolic":"method"}],"authorizationHeader":[{"__symbolic":"method"}],"logOut":[{"__symbolic":"method"}],"createAndSaveNonce":[{"__symbolic":"method"}],"createNonce":[{"__symbolic":"method"}],"checkAtHash":[{"__symbolic":"method"}],"checkSignature":[{"__symbolic":"method"}]}},"JwksValidationHandler":{"__symbolic":"class","extends":{"__symbolic":"reference","name":"AbstractValidationHandler"},"members":{"validateSignature":[{"__symbolic":"method"}],"alg2kty":[{"__symbolic":"method"}],"calcHash":[{"__symbolic":"method"}],"toByteArrayAsString":[{"__symbolic":"method"}]}},"NullValidationHandler":{"__symbolic":"class","members":{"validateSignature":[{"__symbolic":"method"}],"validateAtHash":[{"__symbolic":"method"}]}},"ValidationParams":{"__symbolic":"interface"},"ValidationHandler":{"__symbolic":"class","members":{"validateSignature":[{"__symbolic":"method"}],"validateAtHash":[{"__symbolic":"method"}]}},"AbstractValidationHandler":{"__symbolic":"class","members":{"validateSignature":[{"__symbolic":"method"}],"validateAtHash":[{"__symbolic":"method"}],"inferHashAlgorithm":[{"__symbolic":"method"}],"calcHash":[{"__symbolic":"method"}]}},"UrlHelperService":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Injectable"}}],"members":{"getHashFragmentParams":[{"__symbolic":"method"}],"parseQueryString":[{"__symbolic":"method"}]}},"AuthConfig":{"__symbolic":"class","members":{}},"LoginOptions":{"__symbolic":"class","members":{}},"OAuthStorage":{"__symbolic":"class","members":{"getItem":[{"__symbolic":"method"}],"removeItem":[{"__symbolic":"method"}],"setItem":[{"__symbolic":"method"}]}},"ReceivedTokens":{"__symbolic":"class","members":{}},"ParsedIdToken":{"__symbolic":"interface"},"AUTH_CONFIG":{"__symbolic":"new","expression":{"__symbolic":"reference","module":"@angular/core","name":"InjectionToken"},"arguments":["AUTH_CONFIG"]}},"origins":{"OAuthModule":"./index","OAuthService":"./oauth-service","JwksValidationHandler":"./token-validation/jwks-validation-handler","NullValidationHandler":"./token-validation/null-validation-handler","ValidationParams":"./token-validation/validation-handler","ValidationHandler":"./token-validation/validation-handler","AbstractValidationHandler":"./token-validation/validation-handler","UrlHelperService":"./url-helper.service","AuthConfig":"./auth.config","LoginOptions":"./types","OAuthStorage":"./types","ReceivedTokens":"./types","ParsedIdToken":"./types","AUTH_CONFIG":"./tokens"},"importAs":"angular-oauth2-oidc"} |
@@ -1,2 +0,2 @@ | ||
export declare type EventType = 'discovery_document_loaded' | 'received_first_token' | 'jwks_load_error' | 'invalid_nonce_in_state' | 'discovery_document_load_error' | 'discovery_document_validation_error' | 'user_profile_loaded' | 'user_profile_load_error' | 'token_received' | 'token_error' | 'token_refreshed' | 'token_refresh_error' | 'silent_refresh_error' | 'silently_refreshed' | 'silent_refresh_timeout' | 'token_validation_error' | 'token_expires'; | ||
export declare type EventType = 'discovery_document_loaded' | 'received_first_token' | 'jwks_load_error' | 'invalid_nonce_in_state' | 'discovery_document_load_error' | 'discovery_document_validation_error' | 'user_profile_loaded' | 'user_profile_load_error' | 'token_received' | 'token_error' | 'token_refreshed' | 'token_refresh_error' | 'silent_refresh_error' | 'silently_refreshed' | 'silent_refresh_timeout' | 'token_validation_error' | 'token_expires' | 'session_changed' | 'session_error' | 'session_terminated'; | ||
export declare abstract class OAuthEvent { | ||
@@ -7,2 +7,4 @@ readonly type: EventType; | ||
export declare class OAuthSuccessEvent extends OAuthEvent { | ||
readonly info: any; | ||
constructor(type: EventType, info?: any); | ||
} | ||
@@ -9,0 +11,0 @@ export declare class OAuthInfoEvent extends OAuthEvent { |
@@ -11,9 +11,12 @@ import { ModuleWithProviders } from '@angular/core'; | ||
import 'rxjs/add/observable/race'; | ||
export * from "./oauth-service"; | ||
export * from './oauth-service'; | ||
export * from './token-validation/jwks-validation-handler'; | ||
export * from './token-validation/null-validation-handler'; | ||
export * from './token-validation/validation-handler'; | ||
export * from "./url-helper.service"; | ||
export * from './url-helper.service'; | ||
export * from './auth.config'; | ||
export * from './types'; | ||
export * from './tokens'; | ||
export declare class OAuthModule { | ||
static forRoot(): ModuleWithProviders; | ||
} |
import { Http, Headers } from '@angular/http'; | ||
import { Observable } from 'rxjs/Observable'; | ||
import { ValidationHandler } from "./token-validation/validation-handler"; | ||
import { UrlHelperService } from "./url-helper.service"; | ||
import { OAuthEvent } from "./events"; | ||
import { OAuthStorage, LoginOptions, ParsedIdToken } from "./types"; | ||
import { ValidationHandler } from './token-validation/validation-handler'; | ||
import { UrlHelperService } from './url-helper.service'; | ||
import { OAuthEvent } from './events'; | ||
import { OAuthStorage, LoginOptions, ParsedIdToken } from './types'; | ||
import { AuthConfig } from './auth.config'; | ||
/** | ||
@@ -12,95 +13,7 @@ * Service for logging in and logging out with | ||
*/ | ||
export declare class OAuthService { | ||
export declare class OAuthService extends AuthConfig { | ||
private http; | ||
private config; | ||
private urlHelper; | ||
/** | ||
* The client's id as registered with the auth server | ||
*/ | ||
clientId: string; | ||
/** | ||
* The client's redirectUri as registered with the auth server | ||
*/ | ||
redirectUri: string; | ||
/** | ||
* An optional second redirectUri where the auth server | ||
* redirects the user to after logging out. | ||
*/ | ||
postLogoutRedirectUri: string; | ||
/** | ||
* The auth server's endpoint that allows to log | ||
* the user in when using implicit flow. | ||
*/ | ||
loginUrl: string; | ||
/** | ||
* The requested scopes | ||
*/ | ||
scope: string; | ||
resource: string; | ||
rngUrl: string; | ||
/** | ||
* Defines whether to use OpenId Connect during | ||
* implicit flow. | ||
*/ | ||
oidc: boolean; | ||
/** | ||
* Defines whether to request a access token during | ||
* implicit flow. | ||
*/ | ||
requestAccessToken: boolean; | ||
options: any; | ||
/** | ||
* The received (passed around) state, when logging | ||
* in with implicit flow. | ||
*/ | ||
state: string; | ||
/** | ||
* The issuer's uri. | ||
*/ | ||
issuer: string; | ||
/** | ||
* The logout url. | ||
*/ | ||
logoutUrl: string; | ||
/** | ||
* Defines whether to clear the hash fragment after logging in. | ||
*/ | ||
clearHashAfterLogin: boolean; | ||
/** | ||
* Url of the token endpoint as defined by OpenId Connect and OAuth 2. | ||
*/ | ||
tokenEndpoint: string; | ||
/** | ||
* Url of the userinfo endpoint as defined by OpenId Connect. | ||
*/ | ||
userinfoEndpoint: string; | ||
responseType: string; | ||
/** | ||
* Defines whether additional debug information should | ||
* be shown at the console. | ||
*/ | ||
showDebugInformation: boolean; | ||
/** | ||
* The redirect uri used when doing silent refresh. | ||
*/ | ||
silentRefreshRedirectUri: string; | ||
silentRefreshMessagePrefix: string; | ||
/** | ||
* Set this to true to display the iframe used for | ||
* silent refresh for debugging. | ||
*/ | ||
silentRefreshShowIFrame: boolean; | ||
/** | ||
* Timeout for silent refresh. | ||
*/ | ||
siletRefreshTimeout: number; | ||
/** | ||
* Some auth servers don't allow using password flow | ||
* w/o a client secreat while the standards do not | ||
* demand for it. In this case, you can set a password | ||
* here. As this passwort is exposed to the public | ||
* it does not bring additional security and is therefore | ||
* as good as using no password. | ||
*/ | ||
dummyClientSecret: string; | ||
/** | ||
* The ValidationHandler used to validate received | ||
@@ -111,26 +24,2 @@ * id_tokens. | ||
/** | ||
* Defines whether https is required. | ||
* The default value is remoteOnly which only allows | ||
* http for location, while every other domains need | ||
* to be used with https. | ||
*/ | ||
requireHttps: boolean | 'remoteOnly'; | ||
/** | ||
* Defines whether every url provided by the discovery | ||
* document has to start with the issuer's url. | ||
*/ | ||
strictDiscoveryDocumentValidation: boolean; | ||
/** | ||
* JSON Web Key Set (https://tools.ietf.org/html/rfc7517) | ||
* with keys used to validate received id_tokens. | ||
* This is taken out of the disovery document. Can be set manually too. | ||
*/ | ||
jwks: object; | ||
/** | ||
* Map with additional query parameter that are appended to | ||
* the request when initializing implicit flow. | ||
*/ | ||
customQueryParams: object; | ||
private discoveryDocumentLoadedSubject; | ||
/** | ||
* Informs about events, like token_received or token_expires. | ||
@@ -140,18 +29,29 @@ * See the string enum EventType for a full list of events. | ||
events: Observable<OAuthEvent>; | ||
/** | ||
* The received (passed around) state, when logging | ||
* in with implicit flow. | ||
*/ | ||
state?: string; | ||
private eventsSubject; | ||
silentRefreshIFrameName: string; | ||
private discoveryDocumentLoadedSubject; | ||
private silentRefreshPostMessageEventListener; | ||
private grantTypesSupported; | ||
private _storage; | ||
/** | ||
* Defines when the token_timeout event should be raised. | ||
* If you set this to the default value 0.75, the event | ||
* is triggered after 75% of the token's life time. | ||
*/ | ||
timeoutFactor: number; | ||
private accessTokenTimeoutSubscription; | ||
private idTokenTimeoutSubscription; | ||
private sessionCheckEventListener; | ||
private jwksUri; | ||
constructor(http: Http, urlHelper: UrlHelperService); | ||
private getKeyCount(); | ||
private sessionCheckTimer; | ||
private silentRefreshSubject; | ||
constructor(http: Http, storage: OAuthStorage, tokenValidationHandler: ValidationHandler, config: AuthConfig, urlHelper: UrlHelperService); | ||
/** | ||
* Use this method to configure the service | ||
* @param config the configuration | ||
*/ | ||
configure(config: AuthConfig): void; | ||
private restartSessionChecksIfStillLoggedIn(); | ||
private restartRefreshTimerIfStillLoggedIn(); | ||
private setupSessionCheck(); | ||
setupAutomaticSilentRefresh(): void; | ||
loadDiscoveryDocumentAndTryLogin(): void; | ||
private debug(...args); | ||
@@ -161,3 +61,3 @@ private validateUrlFromDiscoveryDocument(url); | ||
private validateUrlAgainstIssuer(url); | ||
private setupTimer(); | ||
private setupRefreshTimer(); | ||
private setupAccessTokenTimer(); | ||
@@ -234,3 +134,14 @@ private setupIdTokenTimer(); | ||
silentRefresh(): Promise<OAuthEvent>; | ||
private createLoginUrl(state?, loginHint?, customRedirectUri?); | ||
private canPerformSessionCheck(); | ||
private setupSessionCheckEventListener(); | ||
private handleSessionUnchanged(); | ||
private handleSessionChange(); | ||
private waitForSilentRefreshAfterSessionChange(); | ||
private handleSessionError(); | ||
private removeSessionCheckEventListener(); | ||
private initSessionCheck(); | ||
private startSessionCheckTimer(); | ||
private stopSessionCheckTimer(); | ||
private checkSession(); | ||
private createLoginUrl(state?, loginHint?, customRedirectUri?, noPrompt?); | ||
/** | ||
@@ -240,3 +151,4 @@ * Starts the implicit flow and redirects to user to | ||
* | ||
* @param additionalState Optinal state that is passes around. You find this state in the property ``state`` after ``tryLogin`` logged in the user. | ||
* @param additionalState Optinal state that is passes around. | ||
* You find this state in the property ``state`` after ``tryLogin`` logged in the user. | ||
* @param loginHint | ||
@@ -247,3 +159,2 @@ */ | ||
private storeAccessTokenResponse(accessToken, refreshToken, expiresIn); | ||
private receivedFirstToken; | ||
/** | ||
@@ -260,2 +171,4 @@ * Checks whether there are tokens in the hash fragment | ||
protected storeIdToken(idToken: ParsedIdToken): void; | ||
protected storeSessionState(sessionState: string): void; | ||
protected getSessionState(): string; | ||
private handleLoginError(options, parts); | ||
@@ -262,0 +175,0 @@ /** |
{ | ||
"name": "angular-oauth2-oidc", | ||
"version": "2.0.15", | ||
"version": "2.1.0", | ||
"repository": { | ||
@@ -5,0 +5,0 @@ "type": "git", |
422
README.MD
@@ -26,2 +26,11 @@ # angular-oauth2-oidc | ||
## New Features in Version 2.1 | ||
- New Config API (the original one is still supported) | ||
- New convenience methods in OAuthService to streamline default tasks: | ||
- ``setupAutomaticSilentRefresh()`` | ||
- ``loadDiscoveryDocumentAndTryLogin()`` | ||
- Single Sign out through Session Status Change Notification according to the OpenID Connect Session Management specs. This means, you can be notified when the user logs out using at the login provider. | ||
- Possibility to define the ValidationHandler, the Config as well as the OAuthStorage via DI | ||
- Better structured documentation | ||
## New Features in Version 2 | ||
@@ -34,3 +43,2 @@ - Token Refresh for Implicit Flow by implementing "silent refresh" | ||
## Additional Features | ||
- Logging in via OAuth2 and OpenId Connect (OIDC) Implicit Flow (where user is redirected to Identity Provider) | ||
@@ -47,3 +55,2 @@ - "Logging in" via Password Flow (where user enters his/her password into the client) | ||
## Breaking Changes in Version 2 | ||
- The property ``oidc`` defaults to ``true``. | ||
@@ -70,6 +77,11 @@ - If you are just using oauth2, you have to set ``oidc`` to ``false``. Otherwise, the validation of the user profile will fail! | ||
## Installing | ||
## Setup Provider for OAuthService | ||
``` | ||
npm i angular-oauth2-oidc --save | ||
``` | ||
``` | ||
## Importing the NgModule | ||
```TypeScript | ||
import { OAuthModule } from 'angular-oauth2-oidc'; | ||
@@ -98,87 +110,62 @@ [...] | ||
## Using Implicit Flow | ||
## Configuring for Implicit Flow | ||
This section shows how to use the implicit flow, which is redirecting the user to the auth-server for the login. | ||
This section shows how to implement login leveraging implicit flow. This is the OAuth2/OIDC flow best suitable for | ||
Single Page Application. It sends the user to the Identity Provider's login page. After logging in, the SPA gets tokens. | ||
This also allows for single sign on as well as single sign off. | ||
### Configure Library for Implicit Flow (using discovery document) | ||
To configure the library the following sample uses the new configuration API introduced with Version 2.1. | ||
Hence, The original API is still supported. | ||
To configure the library you just have to set some properties on startup. For this, the following sample uses the constructor of the AppComponent which is called before routing kicks in. | ||
```TypeScript | ||
import { AuthConfig } from 'angular-oauth2-oidc'; | ||
``` | ||
@Component({ ... }) | ||
export class AppComponent { | ||
export const authConfig: AuthConfig = { | ||
constructor(private oauthService: OAuthService) { | ||
// URL of the SPA to redirect the user to after login | ||
this.oauthService.redirectUri = window.location.origin + "/index.html"; | ||
// Url of the Identity Provider | ||
issuer: 'https://steyer-identity-server.azurewebsites.net/identity', | ||
// The SPA's id. The SPA is registerd with this id at the auth-server | ||
this.oauthService.clientId = "spa-demo"; | ||
// URL of the SPA to redirect the user to after login | ||
redirectUri: window.location.origin + '/index.html', | ||
// set the scope for the permissions the client should request | ||
// The first three are defined by OIDC. The 4th is a usecase-specific one | ||
this.oauthService.scope = "openid profile email voucher"; | ||
// The SPA's id. The SPA is registerd with this id at the auth-server | ||
clientId: 'spa-demo', | ||
// The name of the auth-server that has to be mentioned within the token | ||
this.oauthService.issuer = "https://steyer-identity-server.azurewebsites.net/identity"; | ||
// Load Discovery Document and then try to login the user | ||
this.oauthService.loadDiscoveryDocument().then(() => { | ||
// This method just tries to parse the token(s) within the url when | ||
// the auth-server redirects the user back to the web-app | ||
// It dosn't send the user the the login page | ||
this.oauthService.tryLogin(); | ||
}); | ||
} | ||
// set the scope for the permissions the client should request | ||
// The first three are defined by OIDC. The 4th is a usecase-specific one | ||
scope: 'openid profile email voucher', | ||
} | ||
``` | ||
### Configure Library for Implicit Flow (without discovery document) | ||
Configure the OAuthService with this config object when the application starts up: | ||
When you don't have a discovery document, you have to configure more properties manually: | ||
```TypeScript | ||
import { OAuthService } from 'angular-oauth2-oidc'; | ||
import { JwksValidationHandler } from 'angular-oauth2-oidc'; | ||
import { authConfig } from './auth.config'; | ||
import { Component } from '@angular/core'; | ||
``` | ||
@Component({ ... }) | ||
@Component({ | ||
selector: 'flight-app', | ||
templateUrl: './app.component.html' | ||
}) | ||
export class AppComponent { | ||
constructor(private oauthService: OAuthService) { | ||
// Login-Url | ||
this.oauthService.loginUrl = "https://steyer-identity-server.azurewebsites.net/identity/connect/authorize"; //Id-Provider? | ||
constructor(private oauthService: OAuthService) { | ||
this.configureWithNewConfigApi(); | ||
} | ||
// URL of the SPA to redirect the user to after login | ||
this.oauthService.redirectUri = window.location.origin + "/index.html"; | ||
// The SPA's id. Register SPA with this id at the auth-server | ||
this.oauthService.clientId = "spa-demo"; | ||
// set the scope for the permissions the client should request | ||
this.oauthService.scope = "openid profile email voucher"; | ||
// Use setStorage to use sessionStorage or another implementation of the TS-type Storage | ||
// instead of localStorage | ||
this.oauthService.setStorage(sessionStorage); | ||
// To also enable single-sign-out set the url for your auth-server's logout-endpoint here | ||
this.oauthService.logoutUrl = "https://steyer-identity-server.azurewebsites.net/identity/connect/endsession"; | ||
// This method just tries to parse the token(s) within the url when | ||
// the auth-server redirects the user back to the web-app | ||
// It dosn't send the user the the login page | ||
this.oauthService.tryLogin(); | ||
} | ||
private configureWithNewConfigApi() { | ||
this.oauthService.configure(authConfig); | ||
this.oauthService.tokenValidationHandler = new JwksValidationHandler(); | ||
this.oauthService.loadDiscoveryDocumentAndTryLogin(); | ||
} | ||
} | ||
``` | ||
### Home-Component (for login) | ||
### Implementing a Login Form | ||
``` | ||
After you've configured the library, you just have to call ``initImplicitFlow`` to login using OAuth2/ OIDC. | ||
```TypeScript | ||
import { Component } from '@angular/core'; | ||
@@ -188,27 +175,29 @@ import { OAuthService } from 'angular-oauth2-oidc'; | ||
@Component({ | ||
templateUrl: "app/home.html" | ||
templateUrl: "app/home.html" | ||
}) | ||
export class HomeComponent { | ||
constructor(private oAuthService: OAuthService) { | ||
constructor(private oauthService: OAuthService) { | ||
} | ||
public login() { | ||
this.oAuthService.initImplicitFlow(); | ||
this.oauthService.initImplicitFlow(); | ||
} | ||
public logoff() { | ||
this.oAuthService.logOut(); | ||
this.oauthService.logOut(); | ||
} | ||
public get name() { | ||
let claims = this.oAuthService.getIdentityClaims(); | ||
if (!claims) return null; | ||
return claims.given_name; | ||
return claims.given_name; | ||
} | ||
} | ||
``` | ||
``` | ||
The following snippet contains the template for the login page: | ||
```HTML | ||
<h1 *ngIf="!name"> | ||
@@ -233,26 +222,7 @@ Hallo | ||
### Validate id_token | ||
### Calling a Web API with an OAuth-Token | ||
You can hook in an implementation of the interface ``TokenValidator`` to validate the signature of the received id_token and its at_hash property. This packages provides two implementations: | ||
- JwksValidationHandler | ||
- NullValidationHandler | ||
The former one validates the signature against public keys received via the discovery document (property jwks) and the later one skips the validation on client side. | ||
``` | ||
import { JwksValidationHandler } from 'angular-oauth2-oidc'; | ||
[...] | ||
this.oauthService.tokenValidationHandler = new JwksValidationHandler(); | ||
``` | ||
In cases where no ValidationHandler is defined, you receive a warning on the console. This means that the library wants you to explicitly decide on this. | ||
### Calling a Web API with OAuth-Token | ||
Pass this Header to the used method of the ``Http``-Service within an Instance of the class ``Headers``: | ||
``` | ||
```TypeScript | ||
var headers = new Headers({ | ||
@@ -263,261 +233,23 @@ "Authorization": "Bearer " + this.oauthService.getAccessToken() | ||
### Refreshing a Token when using Implicit Flow | ||
If you are using the new HttpClient, use the class HttpHeaders instead: | ||
To refresh your tokens when using implicit flow you can use a silent refresh. This is a well-known solution that compensates the fact that implicit flow does not allow for issuing a refresh token. It uses a hidden iframe to get another token from the auth-server. When the user is there still logged in (by using a cookie) it will respond without user interaction and provide new tokens. | ||
To use this approach, setup a redirect uri for the silent refresh: | ||
``` | ||
this.oauthService.silentRefreshRedirectUri = window.location.origin + "/silent-refresh.html"; | ||
``` | ||
Please keep in mind that this uri has to be configured at the auth-server too. | ||
This file is loaded into the hidden iframe after getting new tokens. Its only task is to send the received tokens to the main application: | ||
``` | ||
<html> | ||
<body> | ||
<script> | ||
parent.postMessage(location.hash, location.origin); | ||
</script> | ||
</body> | ||
</html> | ||
``` | ||
Please make sure that this file is copied to your output directory by your build task. When using the CLI you can define it as an asset for this. For this, you have to add the following line to the file ``.angular-cli.json``: | ||
``` | ||
"assets": [ | ||
[...], | ||
"silent-refresh.html" | ||
], | ||
``` | ||
To perform a silent refresh, just call the following method: | ||
``` | ||
this | ||
.oauthService | ||
.silentRefresh() | ||
.then(info => console.debug('refresh ok', info)) | ||
.catch(err => console.error('refresh error', err)); | ||
``` | ||
When there is an error in the iframe that prevents the communication with the main application, silentRefresh will give you a timeout. To configure the timespan for this, you can set the property ``siletRefreshTimeout`` (msec). The default value is 20.000 (20 seconds). | ||
### Automatically refreshing a token when/ before it expires | ||
To automatically refresh a token when/ some time before it expires, you can make use of the event ``token_expires``: | ||
``` | ||
this | ||
.oauthService | ||
.events | ||
.filter(e => e.type == 'token_expires') | ||
.subscribe(e => { | ||
this.oauthService.silentRefresh(); | ||
}); | ||
``` | ||
By default, this event is fired after 75% of the token's life time is over. You can adjust this factor by setting the property ``timeoutFactor`` to a value between 0 and 1. For instance, 0.5 means, that the event is fired after half of the life time is over and 0.33 triggers the event after a third. | ||
### Callback after successful login | ||
There is a callback ``onTokenReceived``, that is called after a successful login. In this case, the lib received the access_token as | ||
well as the id_token, if it was requested. If there is an id_token, the lib validated it. | ||
``` | ||
this.oauthService.tryLogin({ | ||
onTokenReceived: context => { | ||
// | ||
// Output just for purpose of demonstration | ||
// Don't try this at home ... ;-) | ||
// | ||
console.debug("logged in"); | ||
console.debug(context); | ||
} | ||
```TypeScript | ||
var headers = new HttpHeaders({ | ||
"Authorization": "Bearer " + this.oauthService.getAccessToken() | ||
}); | ||
``` | ||
## Preserving State like the requested URL | ||
## More Documentation | ||
When calling ``initImplicitFlow``, you can pass an optional state which could be the requested url: | ||
See the [documentation](https://manfredsteyer.github.io/angular-oauth2-oidc/angular-oauth2-oidc/docs/) for more information about this library. | ||
``` | ||
this.oauthService.initImplicitFlow('http://www.myurl.com/x/y/z'); | ||
``` | ||
After login succeeded, you can read this state: | ||
``` | ||
this.oauthService.tryLogin({ | ||
onTokenReceived: (info) => { | ||
console.debug('state', info.state); | ||
} | ||
}) | ||
``` | ||
### Custom Query Parameter | ||
You can set the property ``customQueryParams`` to a hash with custom parameter that are transmitted when starting implicit flow. | ||
``` | ||
this.oauthService.customQueryParams = { | ||
'tenant': '4711', | ||
'otherParam': 'someValue' | ||
}; | ||
``` | ||
## Routing with the HashStrategy | ||
If you are leveraging the ``LocationStrategy`` which the Router is using by default, you can skip this section. | ||
When using the ``HashStrategy`` for Routing, the Router will override the received hash fragment with the tokens when it performs it initial navigation. This prevents the library from reading them. To avoid this, disable initial navigation when setting up the routes for your root module: | ||
``` | ||
export let AppRouterModule = RouterModule.forRoot(APP_ROUTES, { | ||
useHash: true, | ||
initialNavigation: false | ||
}); | ||
``` | ||
After tryLogin did its job, you can manually perform the initial navigation: | ||
``` | ||
this.oauthService.tryLogin().then(_ => { | ||
this.router.navigate(['/']); | ||
}) | ||
``` | ||
Another solution is the use a redirect uri that already contains the initial route. In this case the router will not override it. An example for such a redirect uri is | ||
``` | ||
http://localhost:8080/#/home | ||
``` | ||
## Events | ||
``` | ||
this.oauthService.events.subscribe(e => { | ||
console.debug('oauth/oidc event', e); | ||
}) | ||
``` | ||
## Using Password-Flow | ||
This section shows how to use the password flow, which demands the user to directly enter his or her password into the client. | ||
### Configure Library for Password Flow (using discovery document) | ||
To configure the library you just have to set some properties on startup. For this, the following sample uses the constructor of the AppComponent which is called before routing kicks in. | ||
Please not, that this configuation is quite similar to the one for the implcit flow. | ||
``` | ||
@Component({ ... }) | ||
export class AppComponent { | ||
constructor(private oauthService: OAuthService) { | ||
// The SPA's id. Register SPA with this id at the auth-server | ||
this.oauthService.clientId = "demo-resource-owner"; | ||
// set the scope for the permissions the client should request | ||
// The auth-server used here only returns a refresh token (see below), when the scope offline_access is requested | ||
this.oauthService.scope = "openid profile email voucher offline_access"; | ||
// Use setStorage to use sessionStorage or another implementation of the TS-type Storage | ||
// instead of localStorage | ||
this.oauthService.setStorage(sessionStorage); | ||
// Set a dummy secret | ||
// Please note that the auth-server used here demand the client to transmit a client secret, although | ||
// the standard explicitly cites that the password flow can also be used without it. Using a client secret | ||
// does not make sense for a SPA that runs in the browser. That's why the property is called dummyClientSecret | ||
// Using such a dummy secreat is as safe as using no secret. | ||
this.oauthService.dummyClientSecret = "geheim"; | ||
// Load Discovery Document and then try to login the user | ||
let url = 'https://steyer-identity-server.azurewebsites.net/identity/.well-known/openid-configuration'; | ||
this.oauthService.loadDiscoveryDocument(url).then(() => { | ||
// Do what ever you want here | ||
}); | ||
} | ||
} | ||
``` | ||
### Configure Library for Password Flow (without discovery document) | ||
In cases where you don't have an OIDC based discovery document you have to configure some more properties manually: | ||
``` | ||
@Component({ ... }) | ||
export class AppComponent { | ||
constructor(private oauthService: OAuthService) { | ||
// Login-Url | ||
this.oauthService.tokenEndpoint = "https://steyer-identity-server.azurewebsites.net/identity/connect/token"; | ||
// Url with user info endpoint | ||
// This endpont is described by OIDC and provides data about the loggin user | ||
// This sample uses it, because we don't get an id_token when we use the password flow | ||
// If you don't want this lib to fetch data about the user (e. g. id, name, email) you can skip this line | ||
this.oauthService.userinfoEndpoint = "https://steyer-identity-server.azurewebsites.net/identity/connect/userinfo"; | ||
// The SPA's id. Register SPA with this id at the auth-server | ||
this.oauthService.clientId = "demo-resource-owner"; | ||
// set the scope for the permissions the client should request | ||
this.oauthService.scope = "openid profile email voucher offline_access"; | ||
// Set a dummy secret | ||
// Please note that the auth-server used here demand the client to transmit a client secret, although | ||
// the standard explicitly cites that the password flow can also be used without it. Using a client secret | ||
// does not make sense for a SPA that runs in the browser. That's why the property is called dummyClientSecret | ||
// Using such a dummy secreat is as safe as using no secret. | ||
this.oauthService.dummyClientSecret = "geheim"; | ||
} | ||
} | ||
``` | ||
### Fetching an Access Token by providing the current user's credentials | ||
``` | ||
this.oauthService.fetchTokenUsingPasswordFlow('max', 'geheim').then((resp) => { | ||
// Loading data about the user | ||
return this.oauthService.loadUserProfile(); | ||
}).then(() => { | ||
// Using the loaded user data | ||
let claims = this.oAuthService.getIdentityClaims(); | ||
if (claims) console.debug('given_name', claims.given_name); | ||
}) | ||
``` | ||
There is also a short form for fetching the token and loading the user profile: | ||
``` | ||
this.oauthService.fetchTokenUsingPasswordFlowAndLoadUserProfile('max', 'geheim').then(() => { | ||
let claims = this.oAuthService.getIdentityClaims(); | ||
if (claims) console.debug('given_name', claims.given_name); | ||
}); | ||
``` | ||
### Refreshing the current Access Token | ||
Using the password flow you MIGHT get a refresh token (which isn't the case with the implicit flow by design!). You can use this token later to get a new access token, e. g. after it expired. | ||
``` | ||
this.oauthService.refreshToken().then(() => { | ||
console.debug('ok'); | ||
}) | ||
``` |
@@ -1,2 +0,2 @@ | ||
import { AbstractValidationHandler, ValidationParams } from "./validation-handler"; | ||
import { AbstractValidationHandler, ValidationParams } from './validation-handler'; | ||
/** | ||
@@ -3,0 +3,0 @@ * Validates the signature of an id_token against one |
@@ -1,2 +0,2 @@ | ||
import { ValidationHandler, ValidationParams } from "./validation-handler"; | ||
import { ValidationHandler, ValidationParams } from './validation-handler'; | ||
/** | ||
@@ -3,0 +3,0 @@ * A validation handler that isn't validating nothing. |
@@ -13,11 +13,11 @@ export interface ValidationParams { | ||
*/ | ||
export interface ValidationHandler { | ||
export declare abstract class ValidationHandler { | ||
/** | ||
* Validates the signature of an id_token. | ||
*/ | ||
validateSignature(validationParams: ValidationParams): Promise<any>; | ||
abstract validateSignature(validationParams: ValidationParams): Promise<any>; | ||
/** | ||
* Validates the at_hash in an id_token against the received access_token. | ||
*/ | ||
validateAtHash(validationParams: ValidationParams): boolean; | ||
abstract validateAtHash(validationParams: ValidationParams): boolean; | ||
} | ||
@@ -24,0 +24,0 @@ /** |
@@ -46,6 +46,6 @@ /** | ||
*/ | ||
export interface OAuthStorage { | ||
getItem(key: string): string | null; | ||
removeItem(key: string): void; | ||
setItem(key: string, data: string): void; | ||
export declare abstract class OAuthStorage { | ||
abstract getItem(key: string): string | null; | ||
abstract removeItem(key: string): void; | ||
abstract setItem(key: string, data: string): void; | ||
} | ||
@@ -52,0 +52,0 @@ /** |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
188750
17
4436
248