I'm doing everything in TypeScript, so there may be some differences in usage from pure JavaScript.
I'm not using this repository directly, I'm using a fork that was made for the app before it was handed to me, and modified to be compatible with a GLUU server: https://github.com/gastate/oidc-client-js
All signin methods require at least these two steps:
- Make the browser load a URL on the authentication server
- Handle the redirect when the authentication server navigates the browser back to your redirect handler
The redirect will have a query string that this library can decode into user information
There are 3 variations of this process:
signinRedirect
, where the library navigates the browser away from your page to the login form on the authentication serversigninSilent
, where the library generates a hidden iframe into which the browser loads a URL on the authentication serversigningPopup
, where the library opens a popup window with a URL on the authentication server
It may not be possible to distinguish which variation is being used from the query string generated buy your authentication server, so you should expect to need a separate redirect handler URL for each variation that you use.
In this variation the browser leaves your page entirely, then comes back to it after the user has successfully authenticated. This always works to get user information.
The URL that the user will be sent back to is the redirect_uri
given when constructing the UserManager
on which you call signinRedirect
. If signinRedirect
is given a parameter that has a state
property, the value of that property will be reconstructed later on the User
object.
The page that loads at redirect_uri
can generate a User
by reconstructing the UserManager
and invoking signinRedirectCallback
with the query string that came from the authentication server. The state
property on the User
object will be the state
property of the parameter given to signinRedirect
.
signinRedirectCallback
causes the User
object to be generated, but the promise it returns resolves to null
, so the object must be retrieved by other means. If User
generation fails, the promise will reject with a non-null error.
In this variation the library generates a hidden iframe into which the browser loads a URL on the authentication server, then redirects the iframe to your page. This only works to get user information when the authentication server is able to recognize an active session for the browser without any user interaction.
The URL that the iframe will be redirected to is the silent_redirect_uri
given when constructing the UserManager
on which you call signinSilent
. If signinSilent
is given a parameter that has a state
property, the value of that property will be reconstructed later on the User
object.
The page that loads at silent_redirect_uri
cannot generate a User
itself; the iframe is hidden and temporary, so that probably wouldn't be useful anyway. What it can do is trigger the generation of a User
in the parent page by reconstructing the UserManager
and invoking signinSilentCallback
with the query string that came from the authentication server. The state
property on the User
object will be the state
property of the parameter given to signinRedirect
.
signinRedirect
causes the User
object to be generated, but the promise it returns resolves to null
, so the object must be retrieved by other means. If User
generation fails, the promise will reject with a non-null error.
Since the methods that trigger generation of a User
object do not return that object (or a promise that resolves to that object, the generated User
object must be retrieved by one of these means
-
The
getUser
method onUserManager
returns a promise that resolves to aUser
object. This can be done any time after generation, and must be done again if otherUser
objects are generated later. IfgetUser
is called before generation, it may resolve to a staleUser
with invalid tokens. -
The
events.addUserLoaded
method onUserManager
takes a callback function as a parameter, and thereafter whenever aUser
is generated the callback will be invoked with thatUser
as a parameter. This can be done any time before generation, and does not need to be done again if otherUser
objects are generated later (the same callback will be invoked again).
For me, being in a violently asynchronous environment and required to not call getUser
until after generation, I'm just not calling getUser at all. Unfortunately, since the methods that trigger generation don't also provide the generated User
, and I have a class that abstracts this whole login process away from the rest of the app (and handles the various callbacks) where I want to have methods that always resolve to some user information regardless of whether generation is necessary, I ended up having to make this library to "convert" events to promises.
The library makes some attempt to save user information where it can be reloaded without making network requests. If getUser
is called before triggering generation it might return a stale User
with expired access tokens. If you get a User
from getUser
before triggering generation, do not assume the access tokens are valid. There are at least 2 ways to ensure that you don't use an expired User
:
-
Check the
expired
andexpires_in
properties on theUser
object. These can tell you if the user has expired, but cannot tell you if the user has logged out in another tab and thereby invalidated the tokens. -
Just do a
signinSilent
before you even think about callinggetUser
. If it resolves, theUser
you get immediately after is valid; if it rejects you need to do an interactive login e.g. withsigninRedirect
.
The querySessionStatus
method on UserManager
may be able to give complete status information, but it performs a callback to silent_redirect_uri
that has a very different query than signinSilent
, and somehow while I was doing that I didn't think to pass that to signinSilentCallback
(probably because I hadn't figured out that the different URLs MUST be distinct and was trying to detect based on the query). But it seems silly to do a querySessionStatus
which does almost the same thing as a signinSilent
but might have to be followed by a signinSilent
anyway.
... But I've also seen cases where signinSilent
seems to generate a user with invalid tokens, so I think I'm still missing something here.
Your callback URLs must be specified in the authentication server configuration, or the authentication server will not redirect to them.
Some authentication servers take your callback URL and append a query string that starts with "&" without including a "?" anywhere, which generates non-compliant URLs. If your callback URLs already have a query in them, appending a query that starts with "&" results in a compliant URL.
signinRedirectCallback
etc can accept the entire URI in window.location.href
; it isn't necessary to extract the query string first, and they will find the information in a non-compliant URL which is missing the "?"
automaticSilentRenew
amounts to a periodic signinSilent
. If a callback has been given to events.addUserLoaded
, it is invoked when renewal succeeds.