I am maintaining this application which is set up with:
an AngularJS front end
a Spring back end (with Spring Security, configured as per the Keycloak documentation) running on Tomcat
Keycloak as single sign-on solution using OpenID Connect.
The Implicit Flow is implemented like this:
The user navigates to the web app in their browser
The AngularJS web app checks if a (non-expired) JWT token is present in the Session Storage
If not, it redirects to Keycloak to request a token
Keycloak authenticates the user and redirects back to the webapp with the token
The web app stores the token in the Session Storage
Any request the web app makes to the back end API, it inserts the header Authorization: Bearer <token>
So far, so good. However, I noticed that logging out does not work as expected:
The user clicks the logout button in the web app
The web app deletes the token from the Session Storage
The web app redirects to the Keycloak logout endpoint
The user accesses the back end API directly and succeeds!
The reason is that a JSESSIONID cookie is stored and this also authorizes the user to access the API.
My questions:
Why is a JSESSIONID cookie being created? Is it default behaviour of Spring Security? Or did I misconfigure something?
How should I fix the logout issue? Make sure a JSESSIONID cookie is never created? Or implement a logout endpoint in the backend that deletes the session cookie?
NB: I am aware of
the risks of storing credentials in Session Storage;
the Implicit Flow being deprecated.
Related
I have a front end (angular) with login form, a back-end for that angular application as my OAuth client (spring dependency) then I have a third application that is the Authorization server and finally the forth is the ressources server.
So I want to know is there is any way to jump the redirect to /login from the authorization server?
I want to login the user with angular login page, then make a get with my OAuth client (spring) for the authorization code flow and then, since i'm not authenticated, instead of getting redirect, I want to get an error "401" and then send a post request with my OAuth client (spring) to the auth server again to login the user that have sent the data previously in angular login page.
Essentially I just wanted to login to my auth server with a custom page that exists in the front end application and let the backend build specially for that front, take over the flow.
You want seamless integration between login form and the rest of your Angular app? Share your CSS between Angular (public) client and authorization-server embedded (private) one, don't implement login in public client.
You might need to better grasp OAuth2 concepts.
Login, logout and user-registration are authorization-server business. Leave it there. Reasons are related to security and just being future-proof: what if you want to plug additional clients to your system (mobile apps for instance)? Are you going to implement login, logout and user-registration again and again? What if you have to introduce multi-factor authentication at some point? Would you break all clients at once?
Your "backend" (Spring REST API secured with OAuth2) is a resource-server, not a client. Make sure it is configured as so: depends on spring-boot-starter-oauth2-resource-server (directly or transitively). This is where the HTTP status for missing (or invalid) authorization is handled. The way to do it depends on your resource-server being a servlet or reactive app. The libs in the repo linked just before do what you want by default (401 instead of 302).
In your case, client is Angular app. I hope you use an OAuth2 client lib such as angular-auth-oidc-client to handle:
authorization-code flow (redirects to authorization-server, back from authorization-server with authorization-code and tokens retrieval with this authorization-code)
refresh-token flow (automatic access-token refreshing just before it expires)
requests authorization (add Bearer Authorization header with access-token on configured routes)
What my Angular apps do for login is just redirect users to authorization-server "authorization" end-point and then just wait for a redirect back to "post-login URL" with an authorization-code. How this code was obtained is none of their business (can be a login form, a "remember-me" cookie, some biometry, etc.).
Also, I have to admit that I don't use Spring's authorization-server. I prefer mature / feature-full solutions like Keycloak, Auth0, Okta, etc. which come with much more already implemented: multi-factor authentication, integration with LDAP, identity federation for "social" providers (Google, Facebook, Github, etc.), administration UI, ...
As ch4mp said, it's best to leave all the authentication and authorization logic and pages to the Spring Authorization Server, it's pretty easy to customize and configure all the user accessible pages (login, authorize, etc...) with Thymeleaf, you only have to bring in your CSS to unify the design.
There was a great demo in last year's Spring One which seems like something you'd like to achieve, you can find the code in this repo.
They used Spring Cloud Gateway as a means to run the Angular SPA in the flights-web app, configuring it as the OAuth client. Following this path you can route all calls to your backend API through the gateway's WebClient.
Are there another ways of doing logout without redirecting my Frontend to oidc/logout page (it works fine,but isn't it insecure sending idToken and my IDP url to frontend),i have separate front and back end. Like in Keycloak which invalidates session by sending refreshToken? If not,what is a right way of doing logout in my application?
We are sending the ID_Token in POST request directly to the IS Server thus it should not have any security concerns. Using ID_Token as id_token_hint while doing a logout is coming from OIDC specification[1]. This will prevent attackers from logging out users from their accounts because only the real RP can present the valid ID Token.
If you want an alternate way to logout you can make use of session management API[2]. But it is recommended to use the logout endpoint.
[1]https://openid.net/specs/openid-connect-session-1_0.html#RPLogout
[2]https://is.docs.wso2.com/en/5.9.0/develop/session-mgt-rest-api/
Some background:
I am writing a small SPA that will use a back end that I have also written. The JS and the back end API are on the same server.
i.e. SPA will load from foo.com, back end is at foo.com/api
In the past I have always used Spring Security with simple form based login. After logging in the user will get a session cookie. Pretty standard stuff.
For this app I looked into OAuth2 implicit flow. My understanding is the User would load my page, then from the SPA I would direct the user to the authorization endpoint so my app could get a token. The user would be redirected from the authorization endpoint to a login form. After the user authenticated with the form.. they would be redirected back to the authorization endpoint to get the token and possibly grant access to the JS client. After that the user would be redirected to a URL specified by the client, with the new access token as a URL fragment.
I have this working and its all great. The part I don't quite get is this:
When the user is redirected to the login form and they authenticate a session is created on the server that has to at least last long enough for the user to be redirected to the authorization endpoint to get the token. At that point they already have an authenticated session on my server, why not just stop there and use traditional cookie and session based logins?
I have been following a tutorial to create a Spring SSO application which uses Facebook for authentication but creates its own access tokens to secure back-end resources.
The sample application creates a user on first login and stores the user's facebook token for further use (getting data from facebook later on).
My question is that how do I handle token expirations? When the facebook token expires, how do we setup spring security to refresh it? What about our application's token expiration?
You can find my sample project here.
The app in the tutorial only uses the token for authentication (i.e. it uses it once when the user logs in to get the user's personal details), so it's highly unlikely to expire in the time it is being used. Having said that, the OAuth2RestOperations instance that is used to carry out that single request is capable of refreshing the token on its own (if the provider sent it a refresh token and allows the access token to be refreshed by your client).
I have a Spring (3.2) based web app that a user can log into. The site will also provide an API secured via OAuth 2.0. My question then, is how do I go about generating a token for a logged in user?
The underlying idea here is that there will be a mobile app that opens up a web frame to the login page, which will eventually redirect to a url schema with an oauth token that the app will catch and then use for the api calls. Looking at the code for TokenEndpoint, I see that it defers token creation to a list of TokenGranter types. Should I be creating my own TokenGranter extended class, or am I looking at this all wrong?
I ended up writing a controller like this:
OAuthClientRequest request = OAuthClientRequest
.authorizationLocation(csOauthAuthorizeUrl)
.setClientId(csClientId)
.setRedirectURI(
UrlLocator.getBaseUrlBuilder().addSubpath(AUTH_CODE_HANDLER_URL).asUnEscapedString())
.setResponseType("code")
.buildQueryMessage();
UrlUtils.temporarilyRedirect(httpResponse, request.getLocationUri());
return null;
Then handling the code returned. My big problem here was that I had the /oauth/authorize endpoint set to use client credentials. Once I realized that tokens were being issued for the client ID instead of the user, it started to make sense.
So you want to use the Authorization Flow of OAuth. Spring has already support that, if you have configured the spring-security-oauth correctly, you just have to redirect the user/your mobile apps to /oauth/authorize?client_id=xxx&response_type=code this will redirect user to authorization page, if user has not login yet, it will redirect the user to login page then to the authorization page.
After the user completed the authorization process, it will redirect the user to an already registered redirect_url parameter with the authorization_code 'yourapp.com/callback?code=xxxx'.
Your application should exchange this authorization_code with the real token access to /oauth/token?grant_type=authorization_code&code=xxxx&client_id=xxxx&client_secret=xxxx
After that you will receive the token access that can be used to access the resource server.