IdentityServer3 - Contacting IdP after user is authenticated - identityserver3

We have SPA, IdSvr3, and a third party IdP(AAD B2C).
The user authenticates successfully. When the user clicks an edit profile link in the SPA, they are directed to IdSvr using the authorization_endpoint. I pass in an acr_value called "profile."
I believe this is the same problem as this gentleman- https://github.com/IdentityServer/IdentityServer3/issues/2816#issuecomment-217161898
I've tried a few things:
Implemented ICustomRequestValidator.ValidateAuthorizeRequestAsync.- In this method, I retrieve the acr_values. There doesn't appear to be a good way to direct the user from here. There is the RedirectUrl on the ValidatedAuthorizeRequest that does redirect the user, but all the information on that object is from the client request to IdSvr and is not relevant to the IdP.
Perhaps there is a setting on the ValidatedRequest that could be tweaked to force IdSvr to direct over to the identity provider(in this case the IdP is - Microsoft.Owin.Security.OpenIdConnect.OpenIdConnectAuthentication)
Implemented a UserService. If the user is logged in, the events trigger IsActiveAsync and GetProfileDataAsync. In IsActiveAsync I can get the acr value from the HTTP context, and set IsActive to false, forcing IdSvr to go back to the IdP. I can't access the context from OpenIdConnectAuthenticationNotifications.RedirectToIdentityProvider, so I haven't found a way to access the acr value there. Is there a way to pass something into here to check if this should be an edit profile?
Similar to the UserService I've implemented IsAuthenticationSessionValidAsync to invalidate the session and force the idsvr to direct to the Idp. This has the same issue of not being able to access the acr value.
PartialLogin doesn't appear to be the right strategy for this either because it appears to only be triggered from a user who isn't authenticated yet. Perhaps if we force the user to log-out, this could be used to direct them to the IdP? https://identityserver.github.io/Documentation/docsv2/advanced/userService.html
Logging the user out feels like the wrong approach to begin with, but it seems like it could be made to work.
Is there a standard way of intercepting a request and directing it over to the IdP based on something in the request?

Related

What is the proper way to invoke Keycloak's end_session_endpoint

Environment: Keycloak 12.0.4
We plan to allow various OpenIDC-protected applications to initiate a "single sign-out" using Keycloak's end_session_endpoint. In our case, we'd like to provide our own URL that applications will hit when they want to sign-out. That URL will either programmatically invoke the end_session_endpoint or simply redirect the user's browser to that endpoint.
The Keycloak documentation and examples I've seen so far are a bit confusing regarding how to invoke this endpoint. For example, do I need to add a query parameter with the id token as a value? (I noticed, for example, that mod_auth_openidc includes an id_token_hint query param.) I assume this call has to be authenticated, so do I include the client id/secret as the credentials?
Thanks!
You have standard how to call RP-Initiated Logout:
https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout
An RP requests that the OP log out the End-User by redirecting the End-User's User Agent to the OP's Logout Endpoint.
It is a redirect, not API call, because you very likely wants to delete also IDP session (cookies on used Keycloak domain in your case).
Here's some additional information from the keycloak-user group which I think completely answers the question:
You can look at
https://openid.net/specs/openid-connect-session-1_0-17.html#RPLogout.
From our implementation, we rely on either cookies or the
id_token_hint to identify the user and logout sessions. So you don't
necessarily need to send id_token_hint if cookies are sent when
redirecting the user from your application to Keycloak. You can also
send a state param to match if the logout action sent to your
application originated from a valid logout request.
Note, however, that we don't force the id_token_hint and do not ask
the user for confirmation, as per spec. But there are discussions to
introduce a consent page.
In regards to logout, messages are sent to clients through the
backchannel.
Edit: Here's the actual (latest) RP-initated logout spec: https://openid.net/specs/openid-connect-rpinitiated-1_0.html.
The logout URL needs to be build from
the OpenID Connect end session endpoint
a target redirect URI where the user should be sent after logout, provided as query param post_logout_redirect_uri
The encoded ID token as query param id_token_hint
In Spring it can be built like this:
String logoutLink = UriComponentsBuilder.fromUri(endSessionEndpoint)
.queryParam("post_logout_redirect_uri", logoutUrlEncoded)
.queryParam("id_token_hint",
((OidcUser) authentication.getPrincipal()).getIdToken().getTokenValue())
.build().toUriString();

Keycloak: How can user be filtered for a specific OIDC client by role?

We have one realm with many users and multiple OIDC clients configured. We would like to connect another application (OIDC Client) to the realm. Unfortunately, the client cannot check for any attributes or roles to be present.
How can I configure Keycloak to authenticate with a specific OIDC-client but return failing authentication if users have not a specific role?
Generally this type of option will not work, since authorization redirects occur before you know who the user is, and therefore before you know which roles are involved.
WHAT I WOULD DO
Make an authorization redirect perform only the following job:
Signing in the user and returning the user id (sub claim), along with perhaps a couple of scopes
Then manage roles within your app - the second app should call a Web API that does this:
Get claims from the access token, and also from other sources, then use them for identification and authorization
If a user authenticates but is not entitled to use a particular app, detect this via an API call, then present an Access Denied page in the app after login.
To reach this you should implement the AuthenticatorFactory and Authenticator interfaces of Keycloak. Look here for more infos.
In the authenticate() method you will write something similar to this:
If(client == yourClient){
// Check for roles
}
Otherwise, expected behaviour in case of failure.

How to ignore session in specific filters

We have a few filters that run and we are trying to implement the "hybrid" approach mentioned in the documentation.
Users can login through a web form and in most cases that is ok and using the created session works fine, but for /api/** calls we want to force the use of a tokens and ignore an authenticated user who may of come from a form login.
/api/content/** : jwt, api, user
/api/**: noSessionCreation, jwt, api, end
/**: jwt, api, user
We would like the /api/** to ignore any authenticated subjects that come from a session (created by a form login). Right now, if I login through the form page the session carries over to the /api calls and bypasses the need for the token being passed in on the url.
So, once I login from the form these calls will work:
/api/info
/api/info?token=123
/api/content/hello.png <-- This is the only one i want to work based off of the logged in user from the from
Our goal, basing of of the hybrid approach mentioned in the documentation is:
/api/** calls must be stateless and always use the token passed to the call.
/api/content/** can use a session, from a form login or the standard filter chain jwt and api calls
/** can pretty much take in anything - session or token.
Is there a way to recognize and forcibly (in a filter?) ignore authenticated users that come from the session?
Sounds like you would need to implement your own filter.
the noSessionCreation filter will not create a session but does not stop the use of an existing one.

GWT : How to prevent client-side state from being cleared when a redirection (for e.g : OAuth2) happens?

I am implementing OAuth2 authentication for one of my GWT projects (let's call it GWT-app). The application responsible for authentication is a third-party application based on Spring Framework-3.1.2.RELEASE and uses its OAuth2 implementation included in Spring Security-3.1.3.RELEASE (let's call it OAuth-app).
GWT-app is a management application for managing user and stores. Each user has one or more manager accounts. A manager account may have one or more stores attached to it so that he can manage them altogether from one screen of the GWT-app.
It means that the user may potentially have to authenticate many times through different manager accounts to display stores by manager account on the app.
So on the StoreManagement.java page of my GWT-app, I have the following :
private final Map<String, ManagerAndStoresProxy> managerAndStores = Maps.newTreeMap(Ordering.natural());
after authentication, this map should be populated with Manager name and a List of attached stores for each.
OAUTH2 FLOW
The Authorization-code flow is used for authentication. When GWT-app wants to authenticate someone, it redirects the user to a login page on OAuth-app. The user enters his credentials and will be presented with an authorization page asking him whether he wants to allow GWT-app to access his information. If the user clicks on authorize, he is redirected back to the GWT-app with an access token.
THE PROBLEM
The only issue is that since GWT-app is redirecting the user to another application for authentication, the first time the authentication works and the map is populated correctly but if I have to authenticate another account then the previously authenticated account is cleared and can not be retrieved because of the redirection.
The question is then : how can I proceed to persist this map and retrieve it back when a second (third, fourth and so on) authentication flow is over (i.e after the url-redirection of OAuth-app).
WHAT I HAVE TRIED SO FAR
What I did so far is that when the user wants to proceed with authentication from the StoreManagement.java page, he is presented with a popup redirected to the OAuth-app authentication page. The access token is then retrieved from this popup but I have to send it back to the parent window (i.e StoreManagement.java page). This can be done via a HTTP GET parameter but this method reloads the parent page and the result is the same: the previously authenticated account is cleared.
QUESTION
What is the best way to handle this situation without clearing the previous context and without requiring any server-side session mechanism whatsoever ?
The root of the problem is using the authorization code grant; the implicit grant (aka client-side flow) would be a much better fit.
If your app is not limited by old browsers in terms of compatibility then you can try to store all necessary things in HTML5 Web Storage.

WIF - optional authentication

I'm working on a proof of concept app. The basic functionality works, where I can log into one website, link to another site that shares the same STS, and the partner site picks up the credentials properly.
However, the partner site only requests the token if the page that we link to requires authentication (which kind of makes sense I guess).
Ideally, I'd like to link to a partner page that does not require you to be authenticated, BUT if the user IS already authenticated, I'd like to at least be able to recognize them on the partner site.
Currently, if my partner landing page does not require authentication, it doesn't appear that the user is logged in when they arrive. As soon as the user requests a page on the partner site that does require authentication, it then grabs the token without requiring the user to log in.
I've tried playing around with the SecurityTokenReceived and RedirectingToIdentityProvider events, but so far I'm stumped.
Any thoughts are appreciated.
So, the problem you are running up against is in dealing with the SessionAuthenticationModule hijacking the request. This module is responsible for detecting if the user has a valid session (based on a cookie that is written upon a successful redirect from the STS) and if not, to redirect the user to the STS to fetch a valid token. The WSFederationAuthenticationModule supplies the eventing necessary to hook into various stages of the redirection/authentication process.
Based on your description, it sounds like you want the following to happen:
User clicks on a link that redirects to the partner site
At the partner site, the request is intercepted and the system determines if the user is signed-in to the STS or anonymous
If the user has a valid session with the STS, then pull the claims for that user
The problem is, your RP cannot know that the user has a valid session without sending the user to the STS first (the RP's do not talk to each other or the STS. The user's brower is used as the means of communication between the RP's and the STS in the form of WS-Fed directives and SAML tokens passed in the url during redirects). If the user is sent to the STS, then they must authenticate, which could be a problem for anonymous users.
So, I do not think there is a "trick" that you can pull via configuration or interception of the request to determine if the user has a valid session with the STS. You might be able to pass a hint, however, from the referrer that is intercepted by the partner site. This hint could take the form of a parameter on the url that indicates to the partner site that the current user has a valid session and to go ahead and redirect to the STS (absence of this hint would indicate an anonymous user). You could also build a system to "hand-off" knowledge of the signed-in user using a resource that both sites have access to (i.e. database).
As you are sure to learn soon, more often than not, WIF offers pieces to the puzzle, but every situation is different and you have to supply the other pieces on your own.
Hope this helps!