This package exports helpers and a TALY plugin that can be used to take care of OAuth authentication in generated applications. Manually-maintained applications are also supported, regardless of whether they are PFE-driven or not.
When setting up TALY oauth in your application, you need to provide an array of configurations, one per IdP. Each configuration object looks like this:
export interface OauthConfig {
// set to `true` if you want this configuration to be used when calling `authorize` or `retrieveToken` without a specific config
default?: boolean;
// name to identify this IdP configuration. Used to find the correct configuration when calling `authorize` or `retrieveToken` with a configuration name.
name: string;
// the application's client id of the OAuth Identity Provider (IdP)
clientId: string;
// the url to the IdP's authorize endpoint, can usually be found in your IdP configuration
authorizeUrl: string;
// the url where tokens can be retrieved from, can usually be found in your IdP configuration
tokenUrl: string;
// the url where the user should be redirected to after a successful login.
// Needs to be set if `appTokenPageIdOrInternalPath` isn't.
// Will be overwritten by `appTokenPageIdOrInternalPath` if both are set
appTokenUrl?: string;
// The internal path of the generated journey, which TALY will use to infer the url where
// the user should be redirected to after a successful login.
// Needs to be set if `appTokenUrl` isn't. Will overwrite `appTokenUrl` if both are set.
appTokenPageIdOrInternalPath?: string;
// if set, this URL will be used on logout to revoke the access and refresh tokens. Only set this if your IdP supports revocation.
revokeUrl?: string;
// if set, this URL will be used on logout to end the session with the IdP. Only set this if your IdP supports ending sessions.
endSessionUrl?: string;
// the scope that the authentication should grant; depending on the IdP, this will usually be "openid offline_access"
scope: string;
// a list of ACR values that are passed to the IDP during the initial /authorize request
// see https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest for details
acrValues?: string[];
// the bff base url. Only requests to endpoints under this URL will receive the auth header
bffBaseUrl: string;
// by default the access token will be renewed before a request is fired if it expires in less than 5 minutes. With this parameter you can adjust this threshold. Specify in milliseconds
refreshBeforeExpiryThreshold?: number;
}
As described above, you can use this option as an alternative to appTokenUrl
to configure the URL where the user should be redirected to, after a successful login. This option is specially intended to support journeys that are deployed once but integrated in different places. You provide the part corresponding to the internal path of your application, and TALY will infer the full URL.
If your app is a TALY-generated journey, make sure that the value of the field matches the corresponding page id in your journey, e.g., "token".
For manual apps, the value of the appTokenPageIdOrInternalPath
field might be the page id, but it could also be a nested path in your app, like "new-claim/token".
For the appTokenPageIdOrInternalPath
to work, you need to provide a TalyRoutingUtilsService
implementation that can be used to infer the full URL.
If you are on a generated journey, you are all set! 🎉 The TalyRoutingUtilsService
is already provided by the TALY journey.
On the other hand, if you are using TALY oauth in a manually maintained app, you need to provide the TalyRoutingUtilsService
yourself. If you are in a manual PFE application, you need to add this code to your app:
providers: [
//...
{
provide: TalyRoutingUtilsService,
useClass: PfeRoutingUtilsService
}
];
We got you also covered if you are in a manual non-PFE application. You can either provide the default TALY provider:
providers: [
//...
TalyRoutingUtilsService
];
or, if you need a different implementation, you can write and provide your own:
providers: [
//...
{
provide: TalyRoutingUtilsService,
useClass: MyAwesomeRoutingUtilsService
}
];
To benefit from the TALY oauth package in a non-PFE application you need to import the OauthModule
in your main module. To pass the necessary configuration you can either import the module as OauthModule.forRoot()
and pass an object with an array of configurations
(one per identity provider (IdP)) as the only argument, like forRoot({ configurations: [config1, config2] })
or you can provide an OAUTH_CONFIGURATIONS
InjectionToken that holds the array of configurations.
The OauthModule
adds providers for a service and an interceptor that are described in more detail in the next subsections. To properly integrate the OauthService
in a config-driven PFE application please see the section "PFE Integration" below.
This service takes care of storing the authentication tokens and communicates with the OAuth backend. The most important functions that this service exposes are authorize()
and retrieveToken()
:
authorize(oauthConfigName?: string, clientState?: Object)
This function will redirect your user to the configured OAuth login form where he/she can login. After a successful login the auth page is expected to redirect the user to the appTokenUrl
that you passed via the OauthConfig
. The appTokenUrl
should lead to a page in your app that calls the retrieveToken()
function. See below. The optionally given clientState
object will be encoded and passed to the IDP. The app will receive this state as the state
query parameter upon returning back to the application.
retrieveToken(oauthConfigName?: string)
This function will use the code that got passed from the auth form (as query parameter) to retrieve an access token, an ID token, and a refresh token to authorize subsequent backend requests. It is important to call this method immediately after the user returns to your application after he/she logged in. Any further navigation inside your app will most likely remove the query parameters from the URL and without them the tokens can't be retrieved anymore.
logout(oauthConfigName?: string)
This function will log the user out of the IdP referenced by the OAuth configuration. Most importantly, it will remove all IdP-related entries from the sessionStorage
. On top of that, if a revokeUrl
and an endSessionUrl
are configured, both endpoints will be called to revoke the access and ID tokens and end the session with the IdP.
decodeClientState(stateString: string)
This function decodes any previously encoded client state that got passed to the IDP in an authorize()
call. It will return the object that got encoded or an empty object if no encoded client state could be found.
Both methods take the oauthConfigName
as an optional parameter. If set, they will use the corresponding configuration (identified using the name
). If not set, they will take the default
configuration.
This interceptor can be used to ensure that each outgoing BFF Request will contain an Authentication
header that includes the current access token. If the access token is about to expire this interceptor will take care of fetching a new access token using the refresh token. The OauthModule
already adds a provider for this interceptor so you don't need to manually provide it.
The interceptor will automatically pick the first matching OAuth configuration (i.e. the IdP) by bffBaseUrl
and the respective access token. It finds the first configuration whose bffBaseUrl
matches the request URL and that has an access token set (i.e. for which authorize
has been called), and then uses that access token for the Authorization
header.
You can additionally import the OauthPfeIntegrationModule
to easily use the OauthModule
in any PFE application. The OauthPfeIntegrationModule
provides the same static forRoot()
function as the OauthModule
that you can use to pass the configurations. It will take care of properly importing the OauthModule
. The integration module registers these PFE actions that you can use in the PFE config:
OAUTH_AUTHORIZE
This PFE action calls the authorize()
function on the OauthService
. You can pass a json expression as clientStateExpression
that will be read from the current state and will be encoded and put in the state
query param. This query parameter will be available after the IDP redirects back to your application. Use appConfiguration.pfeConfig.urlParametersInState
to store any query parameters in the PFE state to use it for your client state. See the usage note here.
OAUTH_GET_TOKEN
This PFE action should be called from the page in your application that the user lands on after a successful login. See the section about OauthService.retrieveToken()
above for the reasons. This action
also stores the retrieved tokens at the key oauth
in the PFE store in the following format if you want to use them in your application:
{ accessToken: string; idToken: string; refreshToken: string; expiresIn: number; }
OAUTH_LOGOUT
This PFE action will take care of logging out the current user by calling logout()
on the OauthService
.
OAUTH_DECODE_CLIENT_STATE
This PFE action needs to receive an encodedClientStateExpression
as well as a clientStateDestinationExpression
. It reads an encoded client state from the state value at encodedClientStateExpression
and writes the decoded state into the state at clientStateDestinationExpression
.
OAUTH_LOGOUT
This PFE action will take care of logging out the current user by calling logout()
on the OauthService
.
For both actions, you can pass the oauthConfigName
parameter to explicitly set which IdP configuration to use. If you omit the oauthConfigName
, the default
configuration will be used.
See the Usage section for an example to achieve this in PFE-driven applications.
You can use these actions in your pfe.json
. One possible example configuration looks like shown below. The OAUTH_AUTHORIZE
action is triggered when the user leaves the login
page. The appTokenUrl
that is passed via the OauthConfig
in this case needs to point to your app's token
page to ensure that the OAUTH_GET_TOKEN
PFE action is run immediately after a successful login.
🧩 For more details and an implementation example see the oauth recipe that we prepared.
// Navigation Configuration
{
"pages": [
{
"pageId": "login",
"onPageLeaveActions": [
{
"type": "OAUTH_AUTHORIZE",
"oauthConfigName": "My standard Idp"
}
]
},
{
"pageId": "token",
"allowDirectNavigationWithoutState": true,
"onPageEnterActions": [
{
"type": "OAUTH_GET_TOKEN",
"oauthConfigName": "My standard Idp"
}
]
}
]
}
This is an example about how to setup your application to be able to keep a client state while redirecting to and from the IDP
// Navigation configuration
{
"pages": [
{
"pageId": "login",
"onPageLeaveActions": [
{
"type": "OAUTH_AUTHORIZE",
"oauthConfigName": "My standard Idp",
"clientStateExpression": "$.clientState"
}
]
},
{
"pageId": "token",
"allowDirectNavigationWithoutState": true,
"onPageEnterActions": [
{
"type": "OAUTH_DECODE_CLIENT_STATE",
"encodedClientStateExpression": "$.oauth.state",
"clientStateDestinationExpression": "$.clientState"
},
{
"type": "OAUTH_GET_TOKEN",
"oauthConfigName": "My standard Idp"
}
]
}
]
}
// Additional global configuration
{
"appConfiguration": {
"pfeConfig": {
"urlParametersInState": [
{
"key": "userType",
"stateKeyExpression": "$.clientState.userType"
},
{
"key": "state",
"stateKeyExpression": "$.oauth.state"
}
]
}
}
}
The module OauthPfeIntegrationModule
can be used as a TALY Plugin. With this plugin in your journey, you can use the snippets in the above sections in your journey's pfe.json
file to set up a log-in page/flow.
Here is what a plugin configuration in your journey configuration could look like:
{
"plugins": [
{
"package": "@allianz/taly-oauth/pfe",
"version": "latest",
"modules": [
{
"name": "OauthPfeIntegrationModule",
"options": {
"configurations": [
{
"default": true,
"name": "default-auth",
"authorizeUrl": "http://your-idp.com/authorize",
"clientId": "U2q7XvFIBJVSlL3QIJ1ARjiIN4DZc9j9",
"scope": "openid offline_access",
"tokenUrl": "http://your-idp.com/token",
"appTokenPageIdOrInternalPath": "token",
"serviceActivatorEndpoint": "",
"bffBaseUrl": "http://your-bff.com",
"endSessionUrl": "http://your-idp.com/end_session",
"revokeUrl": "http://your-idp.com/revoke"
}
]
}
}
]
}
],
"...": "..."
}
⚠️ Note that the
@allianz/taly-oauth
Plugin should not be used if your generation-target ismodule
. In these cases, the parent app needs to provide a log-in mechanism and the necessary HTTP Interceptor to authenticate the calls made from within your journey.
The TALY OAuth library uses the OAuth2.0 and OpenID Connect (OIDC) protocols to communicate with IdPs. The reference documentation for these protocols provides more detailed information:
logout
method of the OauthService
uses RP-initiated logout.logout
method of the OauthService
uses token revocation.