Incorporate Keycloak login into SPA - reactjs

We're currently evaluating Keycloak as our SSO solution and while it works for our servlet-based applications there's a question regarding our (React-based) SPAs.
What our designers want: as an example let's say we have an email client spa. The user is in the process of writing an email but then gets distracted. When he returns the SSO session has already timed out and a re-login is required. The user should now be presented with a login form and after login it should be possible to send the email that's still in the SPA's local storage (i.e. re-login without restarting the SPA or losing data).
AFAIK Keycloak doesn't provide an authentication-api (for good reasons) and uses a redirect to the login page and back to the application (as I understand it for mobile apps the system browser would be used). If I'm not mistaken that redirect would then mean the SPA is then reinitialized and thus the data would be lost.
So here's the question: is what our designers want possible to do with Keycloak?
If yes, how would it be done? Directly posting to the login-url that Keycloak is using seems like a bad idea since the tokens would probably not be stored correctly and there might be same-origin policy problems. Would doing it inside an iframe or popup-window work?

For someone who comes back to this question,
I think it's better to stick to the best practice for oAuth2/OpenId Connect for SPAs which is currently "Authorization Code Flow" with PKCE.
https://oauth.net/2/pkce/
https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics-13
A normal flow here needs a complete redirect to the auth server and back so your app will completely re-initialize. Or you use check-sso like Sébastien already mentioned with silent mode.
https://github.com/keycloak/keycloak-documentation/blob/master/securing_apps/topics/oidc/javascript-adapter.adoc
You can configure a silent check-sso option. With this feature enabled, your browser won’t do a full redirect to the {project_name} server and back to your application, but this action will be performed in a hidden iframe, so your application resources only need to be loaded and parsed once by the browser when the app is initialized and not again after the redirect back from {project_name} to your app. This is particularly useful in case of SPAs (Single Page Applications).
This way the login will happen in an iframe and the app initializes only once and should preserve state.

Even if it's not considered as a best practice you can turn on Direct Grant Access for your client which enables to login through a REST call.
Anyway, about not loosing the state of your app, this is a bit outside the scope of Keycloak but you should be able to achieve that with having the state in your redirect URL for instance ?
Also, if you don't want your app to automatically reidrects to the login page you can use : keycloak.init({ onLoad: 'check-sso' }) instead of login-required

Related

Refresh token in Desktop App for OAuth2 Authorization Code Flow

I want to use API in my .net WinForms application which are closed by JWT token. Such tokens can be received from the Identity Provider using Authorization Code Flow + PKCE. I worked with such flow before in Web Application and I know how to request code, exchange it on the access token and I used silent refresh approach to refresh the access tokens.
For my clientId I'm not able to use refresh tokens. So, I'm slightly confused how to implement to refresh tokens in the Winforms application.
It's definitely I need to open Login page to allow the customer enter credentials. Then I guess I need to catch code from the Redirect URL. It seems I need to use loopback IP address as a redirect URL and catch request in the application.
But how to set up refreshing process I have no idea. Please help with advice or add links to examples.
For a desktop app you should follow the recommendations from RFC8252. Without a token refresh you will get usability problems. Eg access token expires after 15 or 30 minutes, then user experiences a re-opening of the system browser.
This might be fine for a high security app, such as for banking, but for most apps it is not what you want. Alternatives such as long lived access tokens are bad from a security viewpoint, since the long lived value is then exposed on every API request.
You won't be able to use silent renew via hidden iframes either, since you don't have that much control over the system browser. You can only open it by executing a URL.
So you have these main choices, and maybe you can explain trade offs to stakeholders. You won't be able to implement a workaround in code:
No refresh token, and sub-optimal usability
Refresh token used, so a longer lived API credential is used in a public client
Locking down what can be done with an access token issued to the desktop app - using scopes and claims - is usually the main mitigation of this type of security concern.

How to detect front-channel logout in a second existing, open browser window and redirect the user to a logged-out page?

Say I have two applications open, one in each tab, and they both leverage the same SSO server. The user globally logs out (front-channel) in App A, but App B remains open in another tab. The front-channel takes care of wiping the cookies but ideally there would be a way to redirect App B to a "you've been logged out" page. However, since it's not possible to detect HTTPOnly cookies via javascript, how can the javascript running in App B's tab detect the removal of the cookie and direct the user? I suppose we could write a non-HTTPOnly cookie as well and monitor that but I thought there may be a better solution out there.
IdentityServer4 supports this via the "session status change" spec here:
https://openid.net/specs/openid-connect-session-1_0.html#ChangeNotification
This allows your app to ping (via postMessage calls to an iframe) for changes to the current session and receive a response indicating if it's changed or not. This all happens client-side and it uses a non-HTTP-only cookie which is populated with the current session ID.
The endpoint in question is advertised as check_session_iframe in the /.well-known/openid-configuration
oidc-client-js implements this out of the box but for a server-side app you may have to roll your own.

Redirect to previous angular state after login

I have a scenario where login and logout API's from third party service provider. Redirection to the above API's call happens in my web layer(using Spring) based on the available cookies and using Angularjs as front end.
Coming to my question, How can I redirect to previous Angular state after log-on into my application once user hits logout.
You can go about this two ways that I can think of. If the data is simple enough, you can add it to the url as a query parameter. If it is more involved, then you may want to look into using local storage. You can save the state to local storage, and then retrieve it when you get redirected back into the app. There are some edge cases you will have to consider, but that should work for you.

Using Multiple Angular App and Session Management

I have 4 angular applications one is a landing app which asks user to login and has to redirect the user according to its type
to one of the other 3 applications. I am unable to figure how to should i achieve that.
Have the three apps running on different subdomains. Upon login backend send a redirect response, figuring out what type of user it is?
But this leads to cors Error. Also i am not sure whether the cookie which i am setting will be accessible in all the subdomains or not.
Is there a way out?
You can do a redirect, but it seems like an unnecessary step (and kind of convoluted for this type of application).
Instead of returning a redirect based on login, it seems more straightforward to just return the address you want to redirect to in the response. Trigger a lookup to determine which app you should be directing to (however you're doing that) and then return the address of the app in the response data. From within Angular, you can extract the address from within response.data in $http. (see angular docs). The nice thing here is you also keep routing control and knowledge of state within Angular itself.
As for the apps themselves--instead of a subdomain, you can simply put the apps into different folders on your domain. This deals with CORS and the cookie issue.
Otherwise, you'd need to set a CORS header. You would do this on whatever backend you're sending the requests to--there's usually some sort of library to make it easy, for example, Flask CORS for Flask. If you need to share cookies in this case, this StackOverflow answer discusses one way of doing it (using an intermediary domain).
Generate a security key for the user session with some TTL in an authentication table when you authenticate the user with your App1
Redirect the user to any other app in any domain with this security key where they can query the authentication table and verify the user.
Let these other applications work on their own (in the front end) and communicate with the back-end with the security key when necessary.
Lot of PHP frameworks has built-in support for this mechanism. My favorite is Silex.

Renew a Long-Lived token used at server side with an Angular application and FB SDK

My context:
An AngularJS application using the Javascript Facebook SDK, and my distinct server (REST APIs).
Workflow:
User is logged in the client through the FB SDK using the method FB.login(callback).
This later gives a short-lived token that is then sent to the server in order to transform it to a long-lived token.
I'm interested in the mechanism of refreshing the long-lived token after 60 days.
So, reading the doc, we found this:
Even the long-lived access token will eventually expire. At any point,
you can generate a new long-lived token by sending the person back to
the login flow used by your web app - note that the person will not
actually need to login again, they have already authorized your app,
so they will immediately redirect back to your app from the login flow
with a refreshed token - how this appears to the person will vary
based on the type of login flow that you are using, for example if you
are using the JavaScript SDK, this will take place in the background,
if you are using a server-side flow, the browser will quickly redirect
to the Login Dialog and then automatically and immediately back to
your app again.
If I interpret it well, when user is ALREADY logged in through FB.login(callback), a simple redirect to the Angular Application's login flow would allow to get a new short-lived token.
I imagine that the FB.login is immediately run anew in this case, without user interaction, as written.
I want to test it simply, so what I've done is:
Logged in into the application through FB.login(callback).
Clicked on a dummy link making a simple redirect with: window.location.replace('/');
My application being a single page application, every URL should be considered as the authentication page.
But the FB.login isn't run in the background, as I expected from the doc.
What would be the reason?
Does it work only when the domain making the redirect is distinct from the client? (I just can't test this case right now)
Did I misinterpret the doc?

Resources