We have 3 applications: admin site running IdentityServer, SPA, webapi with rest services.
On the first one (with IdentityServer) we have some aditional admin ui. A requirement exists that the connected user can seamlessly go from our SPA application to this admin ui without authentication. So, from one web application to the other by redirection.
The question is how to keep both 'sessions' in sync so none expires while at least one is in use?
Example of the problem:
Settings of the apps:
spa_web.com - our spa application -> gets the access token valid for 1 hour and a refresh token.
authorityWithIdServer.com - our id provider site -> has a cookie mantaining session set to 1 hour expiry.
Steps:
1. First we go to spaWeb.com.
2. User needs to be authenticated, so is redirected to authorityWithIdServer.com where he fills out the login form.
3. Using authorization code flow, we are redirected back to spaWeb.com and finnaly get the access token and refresh token which are locally stored.
4. We are using only the spa application for a few hours. Our access token is periodically renewed with the refresh token.
5. Now we decide to go to the admin ui present on the authorityWithIdServer.com.
6. We get the login form again to sign in to that application.
Is it anyhow possible to slide the cookie of authorityWithIdServer.com while we are using spaWeb.com in order not to be forced to login again.
You should not be using refresh tokens in client side apps. Change to authorization code and use the silent (prompt=none in an iframe) way of renewing the token. oidc-client-js implements this out of the box along with session monitoring.
If you do the above then since the renewal request happens in the context of the browser via the authorize endpoint and thus authentication with the IDP is done via cookie then any sliding logic will kick in automatically.
Also note that identityserver4 lets you control how frequently a client must interactively authenticate via the authorize endpoint max_age parameter and the UserSsoLifetime client setting.
Related
Does ORY Hydra currently have a feature that verifies if a client is logged in via OpenID Connect? I notice there is an API to logout via front-channel
When a user revisits the identity provider, however, I have no way of knowing if they are currently logged in or not. They could delete their client-side HTTP cookies and then I am out of sync with Hydra. Meaning: Hydra has them as logged in, but I have them now as logged out. Also, in the event of a back-channel logout, I want to be able to query for this state.
Is there an API I am overlooking that allows me to know whether a client currently has an active OpenID Connect login via Hydra?
It appears as of right now the only thing one can do is redirect the user to the authorization endpoint since we have no way of knowing if they are authorized or not.
The following two tables that ship with Hydra seem to be the source of truth for the data I am after: hydra_oauth2_access and hydra_oauth2_authentication_session. Does it ever make sense to query those directly if there is no supported HTTP API out of the box to see if a user has an active authentication session?
Sending an authentication request via a redirect to the Provider including prompt=none addresses this use case: it will silently login and return new tokens if there's an ongoing SSO session at the Provider, it will return an error code login_required if not.
Notice there will never be explicit user interaction in both cases so this is convenient (and meant) to run in an hidden iframe.
LOGGED IN STATE
An OAuth client is most commonly a UI application with multiple users. Each user's logged in state is represented by an Authorization Server session cookie that neither the application or user have access to:
The Authorization Server (AS) issues an SSO cookie, to be stored in the system browser for the AS domain
Both Web UIs and Native UIs send it implicitly on subsequent requests, when they invoke the system browser
AUTHORIZATION REDIRECTS
When an OAuth UI redirects the user, it is generally unknown whether:
The user will be prompted to login
The user will be signed in silently (eg the user could have signed in to another app)
For a Web UI it is possible to send an authorization redirect on a hidden iframe with a prompt=none parameter. If the user needs to sign in a login_required error code will be returned. See my Silent Token Renewal Page for further details.
This is not fully reliable however, and has some browser issues in 2020. Also it may be unsuitable if you are using a different type of client.
FEDERATED LOGINS
In some setups the AS redirects further to an Identity Provider (IDP), and the user's login state is further influenced by an IDP session cookie.
There is no way for an app to get hold of the user's IDP login state, since the app only ever interacts with the AS.
IS THERE A USABILITY PROBLEM?
If so, post back and we can discuss further ...
I currently use Spring Oauth 2 framework for authentication and authorization. When i google on what is the best way to store the access token and refresh token, i was recommended to store the access token in memory such as a variable and store the refresh token in a secured HttpOnly cookie. This was working fine until i faced a new issue.
I opened a new tab next to the tab where i was already logged in, now the problem is instead of directly going into the application, the login page was presented. I now again enter my username and password and login into the second tab without any issues.
But when i do logout from the 2nd tab, both first and second tab gets logged out since the refresh token cookie is shared but the access token which is not shared between tabs since its stored in a variable.
I was expecting following results
When i do a login into my second tab, i expect the session to be separate. The reason i'm setting the refresh token in cookies instead of storing it in a variable like access token because there are multiple applications hosted in the same domain to implement SSO concept. When an another application is clicked instead of routing it to a login page, i just get a new access token for the application using the refresh token that is stored in the http only cookie
I was told the only solution is to store the access token in localstorage or normal cookies so that i can also share the access token between tabs but it seems to be not a secure way as the token being can be stolen using XSS attack.
Hoping for a optimal solution. BTW when the user logsout, the invalidate both the access and refresh token from my JDBC token store
Sounds like you need to decide what type of Web UI you want. Trying to mix and match these concepts does not work well, as you are discovering:
Either your Web UI is a cookieless Single Page Application
Or your Web UI routes all data requests via a web back end using an Auth cookie
TOKEN SCOPE
Generally tokens are private per browser tab and auth cookies are not, as you realise. Using tokens in the Web UI will give you better control of usability aspects.
SPA COOKIELESS MODEL
This gives you independent sessions per browser tab, but requires you to use a library such as oidc client to implement logins client side. You can then store an access token in memory. Token refresh is done via silent iframe redirects and not via refresh tokens.
SPA COOKIELESS COMPONENTS
In this model:
Spring Boot's role would be to host REST APIs
The Web UI would not use Java at all
The Web Back End would be just static content
For a bit more background, and how to implement login / token management in Javascript, see these posts of mine:
SPA Goals
Tutorial + Code Sample
NO WEB UI OPTION IS PERFECT
They all have annoyances and sometimes it depends what your stakeholders most care about.
I am developing mobile application that works with server side using OAuth2. For authorization we use OpenID Connect with authorization code flow. Typical step in the authorization process on mobile devices in this flow is to open authorization url in system browser and then capture redirect url with authorization code.
In case when user is required to enter login and password in browser it is OK. But we have some clients authorized by IP and in this case system browser auto closes immediately after launch and returns successful authorization. Such useless browser launch is annoying and I'd like to prevent it.
The only idea I have right now is to make direct HTTP authorize request with prompt=none parameter as described in https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest to try authorize by IP. And in case of login_required or interaction_required errors repeat it in system browser without prompt parameter.
prompt=none
The Authorization Server MUST NOT display any authentication or
consent user interface pages. An error is returned if an End-User is
not already authenticated or the Client does not have pre-configured
consent for the requested Claims or does not fulfill other conditions
for processing the request. The error code will typically be
login_required, interaction_required, or another code defined in
Section 3.1.2.6. This can be used as a method to check for existing
authentication and/or consent
Is there any other way to detect moment in authorization flow when user interaction with web page is really needed? So I can open browser only if it is needed. And without additional requests useless for most clients.
Since you use OpenID Connect, you might think of enabling some other mechanism to maintain authorized state between application, backend and identity server.
One such scenario could be usage of Access and Refresh tokens,
First login, end user authentication is mandatory
Your app receive tokens (ID token, access token and refresh token)
Customer use the app and app use access token to communicate with the backend
Customer use the application second time
You know previous access token is expired so you obtain a new access token using refresh token
Of course refresh token can expire or identity serve can revoke the access token for some reason (ex- user changing password). But IMO this is the best solution provided by specification itself.
I have an Asp.Net MVC 5 web application that uses OpenId Connect via Azure AD to manage user authentication. I think I understand that once authenticated, the auth token is valid for one hour at which point the OWIN middleware will handle refreshing the auth token in the background without the user having to be redirected to the login page and re-enter their username/password. I have observed this by watching the address change in the address bar of the browser after the auth token expires.
My problem is that I have form that users stay on for a long time (60+ minutes) while they are doing interviews and entering in data. The auth token expires and when the user tries to submit the form the auth token refresh happens in the background and the form is never posted to my application. Instead, the view is refreshed (from the HTTP GET that happens after the auth token is refreshed) and the user loses all of the data they've collected over the last hour.
Is this the expected behavior or do I have something configured incorrectly?
As background, I'm using the Google OAuth2 NodeJS client, but I think my question is more abstract / technology independent.
My website is single-page application that communicates via AJAX to the server.
When a user first visits my website, I perform an OAuth2 flow which redirects them to Google to log in, and then redirects back to my site with an access token. I store this access token in a cookie, and use it to handle various calls made to the server via AJAX.
My challenge is that I'm unsure what to do when that access_token expires. Should I be storing the refresh_token in a cookie as well, and using that, or are there security issues in doing so?
Should I be redirecting the browser to perform the login flow again? That seems fairly ugly for a single-page application.
You can do the OAuth2 flow via js in the background(like the login flow with the popup window), and if the access hasn't been revoked for you app id, then the user shouldn't see anything about it. Although you can set a hint on the user email to authenticate, this may not work.
The other way that you mentioned, is the refresh token, that you can use to ask for a new access token, without user interaction. Maybe that would be the better idea, but remember, that you will only get a refresh token if you set the access type to offline.