Okta Kentor.AuthServices IdentityServer3 IDP-initiated SSO is triggering SP-initiated SSO - error or design? - saml-2.0

Using IdentityServer3, Kentor.AuthServices 0.19 (with OWIN middleware) and a standard MVC 4 WebApi 2 app, we have followed instructions at https://github.com/KentorIT/authservices/blob/master/doc/IdentityServer3Okta.md
and it appeared that we achieved successful IDP-initiated login.
However, when we looked closely at this, and using KentorStubIdp (where we first noticed we were prompted to provide a SAML response), we found the following
IDP hits our endpoint, e.g. identityserver/okta/acs, status 303
Successful redirection to our redirection endpoint in our app, which is coded to return a redirection to the identityserver authorisation endpoint, thus
var client = new AuthorizeRequest(new Uri(identityServerUrl + "connect/authorize"));
var returnUrlForIdp = client.CreateAuthorizeUrl(
"{client_identifier}",
"id_token token",
scopesForAuth,
hostUrl,
state,
nonce,
acrValues: string.Format("idp:{0}", idp),
responseMode: "form_post"
);
return Redirect(returnUrlForIdp);
This results in a 302 to identityserver/connect/authorise. It appears that this has all the login information it needs, and I would have expected a 200 straight into the app, but instead we get a 302 to identityserver/login?signin=xxx which gives a 401 which appears to trigger...
The subsequent call to the login endpoint gets a 303 redirection back to the IDP, which marks the start of an ultimately successful SP-initiated login. Meaning it comes back to identityserver/okta/acs, then the /callback endpoint, then /connect/authorise then the user is logged in.
I cannot find any meaningful difference between the first and second calls to /connect/authorise except
The successful attempt is preceeded by a call to identityserver/callback
Cookies for idsvr and idsvr.session appear not to be set on the first call but are in the second
Also, Kentor config settings seem to be in order - e.g.
AllowUnsolicitedAuthnResponse = true
and
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive although this last one didn't seem to have an effect either way
At this point I'm just trying to work out a) whether this is how it is supposed to work under the covers and b) if not, where should I focus my attention to diagnose the problem.
Is there a particular set of circustances that trigger authservices to initiate an SP-initiated SAML request if a, IDP-initiated one is lacking info?
Any advice much appreciated.

Using Idp-initiated sign on with SAML2 + OIDC is a bit tricky, as OIDC doesn't support it. Which means that IdSrv3 is not really built for that scenario either.
The outline of what you would need is:
Idp sends unsolicited response to IdSrv3/AuthServices.
AuthServices validates response
IdSrv3 establishes log in session on IdSrv3.
User is redirected to client application's login init url
Client app initiates a OIDC sig in towards IdSrv3.
IdSrv3 Single signs on with session established in 3.
User is redirect back to client app.
Looks like step 2 works, but step 3 is not properly done. Which means that in step 6 there is no session, so user is redirected all the way to the Idp to pick up existing session. This works, but is somewhat ugly. And if you later on want to do single sign out there is a session count mismatch which might cause issues.

Related

OKTA Logout SAML App

I have setup an Application that's is using OKTA as IDP. The app is SAML Based.This part is working fine.
But I am unable to log out. For this we have
1. Enabled Single Logout
2. Set the Single Log out URL (I received this from Metadata of IDP under header Identity Provider Single Logout URL)
3.Sp Issues (I received this from Metadata of IDP under header Identity Provider Issuer )
4. Signature Certificate (This is the certificate of IDP)
Now when I call the Logout URL I am receiving 403. On checking the Logs of OKTA I see the (User Single Sign out from App Failure:- Malformed Request)
Can any one please help me how to fix it.
I am assuming that I just need to call the logout URL and the session will kill off. Is my understanding correct?
Reviving a very old thread, check that you have a ?ReturnTo=<path> at the end of the logout URL.
Okta requires strictly post binding requests for logout. Please make sure you are making POST requests for logout and you are using correct entity Id in request.
I think the setting values below need to be set for sp side.
Set the Single Log out URL
Sp Issues
Signature Certificate
It is not on idp side.

How can you implement refresh tokens in a web app - angularjs

I am using token based security in my web app. The server side is wrote using c# and i am using openiddict for logging in and issuing tokens, found here. I am currencyly using Implict flow.
By default my tokens have a lifespan of 1 hour, after that you have to logging again. I have locked down my API to accept bearer tokens only and not cookies.
I wanted to implement refresh tokens but after reading many websites, it appears that implementing refresh tokens on a web app, is not a good way to go due to a hacker getting the refresh token. I know that to use refresh tokens, you must use code flow, instead of implict, which i can do.
How do people get round this situation in their web apps? I cant be the only one who wants a token to last longer than an hour in a web app?
The approach recommended by OpenID Connect is to send an authorization request in a hidden frame with the same parameters as the ones you use for the initial implicit flow request plus prompt=none and optionally, an id_token_hint corresponding to the id_token you extracted from the authorization response.
When using prompt=none, the identity provider won't display any consent form and will directly redirect the user agent to the redirect_uri you specify, with the new token appended to the URI fragment, just like for a classic implicit flow request. You can retrieve it by extracting it from the popup.location.hash property.
If the request cannot be processed (invalid request, unauthenticated user, invalid id_token_hint, consent required, etc.), an error is returned and the identity provider either redirects the user agent to the redirect_uri with an error parameter or stops processing the request.
Note that due to the same origin policy, you can't access popup.location.hash if the current location belongs to a different domain (e.g if the identity provider refuses to redirect the user agent to your client app): it will throw an access denied exception. In this case, it's always better to add a timeout to your "refresh" operation.
Sadly, there are very few libraries that can help you with this task. oidc-token-manager is one of them, but it has a few limitations that will prevent it from working OTB with OpenIddict: it doesn't support raw RSA keys (you have to explicitly use a X509 certificate in the OpenIddict options) and it doesn't send the id_token_hint parameter required by OpenIddict when sending a prompt=none request.

Google OAuth2 flow and id_token refresh

I am having troubles in implementing OAuth in the right way.
I use a client/API architecture (Angular for front and Node.js for back) and I would like user to sign in using Google OAuth authentication only.
Here is what I think is the right way for the moment (tell me if I misunderstood something) :
Angular open a Google popup asking user's consent.
Once the user agree, Google Authorization server sends back to angular a verification code.
This verification code is forwarded to an API endpoint.
Then, the API asks Google Authorization server to exchange this code for an access_token, an id_token and a refresh_token.
Google sends those 3 tokens.
The API uses access_token to retrieve user from Google API
The API persists the user
Here is the little dillema, in my opinion, the access_token and refresh_token should be stored into the database and the id_token should be sent back to Angular client.
This way, it would allow the API to ask for resource in Google API and if the token expires it can still ask for a new token thanks to the refresh_token.
Client-side, the id_token is embedded in all requests thus allowing the API to identify the client and verify his authentication with Google certs from https://www.googleapis.com/oauth2/v3/certs.
Supposing this is right way to use tokens, how could I deal with id_token expiration since client does not have any refresh token ?
Thanks !
I do it slightly different (I have the same basic architecture though).
Angular decides the user needs to log in and displays a login popup.
The url in the login popup is not serviced by angular, but is instead directly run off of the backend server: /auth/google . (I use hapijs and bell, personally).
/auth/google is serviced by a bell plugin and initiates the OAUTH dance.
the end of the OAUTH dance results in my node server generating a local token (I just generate random bytes and store them in redis mapped to user ids)
because the initial login popup was created by window.open, the success page (generated on the api side rather than in angular) can use window.opener.postMessage to communicate the token back to the angular runtime.
This way, all my sensitive google credentials (the user's oauth token, refresh token if needed, and my application's api ID and secret) are only on the server, except for during the OAUTH dance relay when they're in a URL string during the client redirects. This is reasonably secure.
Then for all the actual user interactions with the api, I use the token I generated in step four to authenticate. This could be a JWT if you wanted, but I don't do it that way; I just use redis to map from 'longrandostring' -> userId. That lets me (for example) force everyone to re-login if I wipe the redis database that has all the tokens stored, or I can write a lua script to delete all the entries that map to a certain userid.
If you need a refresh token, you can set access_type=offline in the initial request to oauth2/auth, and you'll get a refresh token as part of the response, unless you've previously gotten a refresh token. You can then persist it on the server side and get new access tokens as needed. If you set approval_prompt=force as well, you'll force a new consent screen and be guaranteed a refresh token (but after some small number of refresh tokens granted to a user, older ones expire on the same application so it's best to only request them if really needed).

How to use SAML authentication in a mobile application?

I'm trying to understand how an saml authentication flow could work in a mobile environment where the client (AngularJS based), api server (Node & passport based), and idp exist on different domains.
From what I've gathered the general practice is to have the server return a 401 to the client if there's no authentication present (i.e. the client didn't include a bearer token in the request). The client understands that a 401 response indicates to open up the login endpoint on the server. When the login endpoint is opened it makes a passport call to the auth provider (which redirects the user to the auth provider's site) and supplies a callback URL. When the user authenticates, the auth provider redirects to the provided callback URL, which allows the server to retrieve information from the auth provider's response and construct a token of some sort (e.g. JWT) that can be used by the client (i.e. included in the headers) when making REST calls to identify itself.
My question is: How does the client get the token from the server? Because we're in a redirect-based authentication flow, I can't just return token from the callback function; that would just display the token in the browser without handing it off of to the client. Does the server just issue a 302 redirect pointing back to the client domain and include the authentication token in a header? Maybe I should not redirect from the client to the server in the first place and instead window.open() and use window.opener.postMessage or is that too old fashioned/mobile-unfriendly?
This question talks about authentication against a SAML IDP, but I'm interested in getting more details specifically about that last bullet point and how it would work with an AngularJS-based client.
Many examples I've seen online are either a single domain using OAuth/SAML (passport-saml-example), which avoids the issue of having the client exist on a separate domain, or use two domains with basic authentication, which avoids the issue of redirecting to some third party for authentication, but I'm having trouble finding good examples that uses all the bits and pieces I'm trying to work with.
This blog post seems very close to what I'm trying to accomplish (see googleSignInCallback) and uses a 302 redirect like I imagined but that solution relies on explicitly knowing the client URL to redirect to, which seems like it could be problematic if I wanted to support multiple client types (i.e. Native applications) in the future.
Eventually I was able to work together a solution by having my application open a browser window (Cordova's InAppBrowser) to a SAML-enabled application, have that application complete the normal SAML flow, and then that SAML-enabled application generated a JWT. My mobile application was then able to extract the JWT string from the browser window with the InAppBrowser's executeScript functionality. Then I could pass that JWT string along to my API server, which was able to validate the JWT is properly signed and trusted.
After I implemented my solution I saw that there was similar functionality available on github:
https://github.com/feedhenry-templates/saml-service
https://github.com/feedhenry-templates/saml-cloud-app
https://github.com/feedhenry-templates/saml-cordova-app
Hopefully this helps anyone else trying to deal with this issue!

Spring Security SAML assertion expiry with Application Session Expiry

I'm getting confused with the SAML assertion expiry vs Application session expiry.
In simple words, when we have an application deployed in a container, there is a session created. This session expiry can be controlled with the below entry in web.xml
<session-config>
<session-timeout>60</session-timeout>
</session-config>
Moving on, when I have Spring Security with SAML extension, obviously the same session concept applies. (I'm deploying the application in WildFly 8.2, if that matters)
Further, when the application session expires, the logout behaviour seems to be equivalent to Local Logout concept.
So far so good. Now lets say that the SAML assertion is good for 2 hours and the user has been actively working for 2 hours. What should happen to the subsequent request then? Should it re-login to the IDP? But, wouldnt that be inconvenient to the user? If the application redirects to IDP for logging in again after 2 hours of assertion expiry, How should AJAX requests be handled?
This is in reference to the question here
Spring SAML issues an ExpiringUsernameAuthenticationToken for authenticated users. The token starts returning false in its isAuthenticated() method once the SAML Assertion used to authenticate the user reaches its sessionNotOnOrAfter time.
This behavior can be disabled by overriding SAMLAuthenticationProvider and changing method getExpirationDate(credential), which returns time when the Assertion expires, or null in case it never does. Application will then fully rely on session expiration configured in the container.
Once the ExpiringUsernameAuthenticationToken expires, Spring Security will pass the current token to the AuthenticationManager (configured in securityContext.xml under <security:authentication-manager>).
You can affect what happens next, by adding your own AuthenticationProvider able to handle the ExpiringUsernameAuthenticationToken. Otherwise system fails with ProviderNotFoundException or some other AuthenticationException like BadCredentialsException (in case you're using username/password authentication at the same time).
The exception is subsequently handled by ExceptionTranslationFilter, which start new authentication process by invoking the configured authentication EntryPoint - e.g. SAMLEntryPoint which either starts authentication with default IDP or displays IDP selection page. The process will also essentially perform local logout, as you say.
System by default behaves the same for all HTTP calls - AJAX or not. You can define different behavior by splitting your API and normal URLs into separate <security:http> elements and use different EntryPoints (interface AuthenticationEntryPoint) for each. For example Http403ForbiddenEntryPoint might be suitable for your AJAX calls.

Resources