How do I configure Spring Security SAML to work with Okta? - spring-security

I'm trying to make spring-boot-security-saml-sample application work with Okta. To add Okta as a provider, I've made the following changes to WebSecurityConfig.java:
https://gist.github.com/mraible/c8b52972f76e6f5e30d5
I found the following question that provides some guidance, but I can't quite get things to work.
configuring saml-sample (SP) to work with Okta (IdP)
Here's what I'm using for values on Okta:
Application label: Spring Boot SAML App
Force Authentication: false
Post Back URL: http://localhost:8080/
Name ID Format: EmailAddressRecipient
Recipient: http://localhost:8080/saml/SSO/alias/defaultAlias
Audience Restriction: com:vdenotaris:spring:sp
authnContextClassRef: PasswordProtectedTransport
Response: Signed
Assertion: Signed
Request: Compressed
Destination: http://localhost:8080/saml/SSO/alias/defaultAlias
Default Relay State: (none)
Attribute Statements: email|${user.email},firstName|${user.firstName}
It looks like it works from the logs:
[2014-12-30 12:18:33.004] boot - 18748 DEBUG [http-nio-8080-exec-8] --- BaseMessageEncoder: Successfully encoded message.
[2014-12-30 12:18:33.004] boot - 18748 DEBUG [http-nio-8080-exec-8] --- HttpSessionStorage: Storing message a12gf64fh3f35fgh2a8dd1fd0i0dc02 to session C5D010344EF5D022718B12B6D25F1D1E
[2014-12-30 12:18:33.004] boot - 18748 INFO [http-nio-8080-exec-8] --- SAMLDefaultLogger: AuthNRequest;SUCCESS;0:0:0:0:0:0:0:1;com:vdenotaris:spring:sp;http://www.okta.com/k2gpb06TOMYOKAWUSXJM;;;
However, it redirects me to Okta's site rather than back to my site.

I got it to work! The key appears to be setting Request to "Uncompressed". From there, I removed "alias/defaultAlias" since this only seems to work when you set an alias on the ExtendedMetadata. My settings that work on the Okta side:
Application label: Spring Boot SAML App
Force Authentication: false
Post Back URL: http://localhost:8080/saml/SSO
Name ID Format: EmailAddressRecipient
Recipient: http://localhost:8080/saml/SSO
Audience Restriction: com:vdenotaris:spring:sp
authnContextClassRef: PasswordProtectedTransport
Response: Signed
Assertion: Signed
Request: Uncompressed
Destination: http://localhost:8080/saml/SSO
Default Relay State: (none)
Attribute Statements: email|${user.email},firstName|${user.firstName}

Matt,
Try setting the "Post Back URL" to "localhost:8080/saml/SSO/alias/defaultAlias".
From the looks of your configuration "localhost:8080/saml/SSO/alias/defaultAlias" is the SAML endpoint on "localhost" which is where we post the SAML Response to.
Right now with it being "localhost:8080/" - your demo site is probably just redirecting you back to Okta rather than parsing the SAML response.
You haven't mentioned what you have done on the Okta side to test this out. Here is the instructions on how to do it - https://support.okta.com/entries/27560008-Using-the-App-Integration-Wizard - using our App Wizard which creates the proper SAML IDP endpoints on the okta side. The SAML login URL on the Okta side is needed by your demo site so that it knows where to redirect SAML requests to.
For more info on SAML - you can check out our SAML guidance on our developer site - http://developer.okta.com/docs/getting_started/saml_guidance.html
Let me know how it goes. Cheers
Stephen

Related

Cortana - OAuth2 Redirect URL Configuration not changing

I'm currently implementing an OAuth2 authentication using Microsoft Bot Framework and Cortana as one of my channels. However, as I was setting up my OAuth2 configuration with the following details in where I properly set the Redirect URL both from Knowledge Store and apps.dev.microsoft.com
Knowledge Store:
apps.dev.microsoft.com:
Whenever I authenticate to Cortana based from the OAuth2 that I've configured, the redirect URI seems to be always set as https://www.bing.com/agents/oauth. Here's a screenshot of the http request from Cortana Authentication that I got from fiddler:
Which causes this error message:
Any idea how to fix this?
Don't forget that the bot channel (in this case Cortana) needs to be where the redirect points to. Cortana's redirect is https://www.bing.com/agents/oauth.
Documentation here. You can test OAuth via botframework and the emulator. In that case, the redirect is https://token.botframework.com/.auth/web/redirect. Documentation here. If you look at the diagram in the spec on page 10, you'll see that Cortana is the client. The auth call needs to come back to her. You also need to let the auth server know that the redirect URL is allowed. For Microsoft login, you go to the app dev portal, select your app, go add a "web platform" and register the redirect urls. That should solve the problem on both ends.

Configuration of Microsoft Graph OAuth2 authentication in Spring Security - Error AADSTS90014

I am writing an SSO provider for MS Graph APIs Azure AD v2 endpoint leveraging Spring OAuth2.
I am progressing with the implementation and constant testing but I stumbled upon an error returned by AAD which is puzzling me. After all, this should all be plain standard OAuth 2 flow.
I successfully configured my application on MS dev portal, providing a localhost redirect URL (which, for the record, is the only supporting the http scheme. Kudos to MS). So when I invoke http://localhost/myapp/auth/office365 Spring security successfully intercepts the invocation, provides a correct redirect to my browser with client ID to https://login.microsoftonline.com/common/oauth2/v2.0/authorize with expected parameters.
Microsoft shows a consent screen to me, after which I get redirected back to my Spring Security application via HTTP GET with expected authorization code parameter.
The problem is that when the application tries to negotiate the given authorization code for a bearer token headaches start. Spring Security invokes a POST to https://login.microsoftonline.com/common/oauth2/v2.0/token but ends in 401 error.
Here is the stack trace
error="invalid_request", error_description="AADSTS90014: The request body must contain the following parameter: 'client_id'.
Trace ID: 9acd2a10-1cfb-443f-9c57-78d608c00c00
Correlation ID: bf063914-8926-4e8f-b102-7522d0e3b0af
Timestamp: 2017-10-09 15:51:44Z", correlation_id="bf063914-8926-4e8f-b102-7522d0e3b0af", error_codes="[90014]", timestamp="2017-10-09 15:51:44Z", trace_id="9acd2a10-1cfb-443f-9c57-78d608c00c00"
at org.springframework.security.oauth2.common.exceptions.OAuth2ExceptionJackson2Deserializer.deserialize(OAuth2ExceptionJackson2Deserializer.java:100)
at org.springframework.security.oauth2.common.exceptions.OAuth2ExceptionJackson2Deserializer.deserialize(OAuth2ExceptionJackson2Deserializer.java:33)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3072)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:235)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readInternal(AbstractJackson2HttpMessageConverter.java:215)
at org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:193)
at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport$AccessTokenErrorHandler.handleError(OAuth2AccessTokenSupport.java:235)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:700)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:653)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:621)
at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.retrieveToken(OAuth2AccessTokenSupport.java:137)
at org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.obtainAccessToken(AuthorizationCodeAccessTokenProvider.java:209)
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainNewAccessTokenInternal(AccessTokenProviderChain.java:148)
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:121)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173)
at org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter.attemptAuthentication(OAuth2ClientAuthenticationProcessingFilter.java:105)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
I have looked into Spring security implementation to find the cause,.
It happens that the error message error="invalid_request", error_description="AADSTS90014: The request body must contain the following parameter: 'client_id'. is self explanatory: MS Graph wants the client ID (which is still supplied by the basic authentication header) in the request body. Stop for a moment. I want to use plain old Spring Security and not third-party specific jars in order not to pollute my classpath.
Looking into Java source code of Spring OAuth 2 the problem is damn clear. Spring uses the client ID only in getParametersForAuthorizeRequest, which is used to generate the redirect URL. When it comes to getParametersForTokenRequest the client ID is not specified in the form.
Question: who is right here? How do I tell Spring that MS wants the client id in the token request after an authorization code has been obtained?
Just to clarify, you're not actually authenticating with or against Microsoft Graph. You're actually authenticating against Azure Active Directory. The Microsoft Graph API accepts the bearer token you'll end up with but it doesn't issue the access token itself.
It isn't clear which endpoint you're using for the Authorization Code flow, AAD has two of them: v1 and v2. The primary difference being that v2 uses a central registration and can authenticate both work/school and personal accounts.
Regardless of the endpoint, you do need to supply the clientid in the request body when you're requesting an access token. There are actually several values you need to provide in the body. Also note that these need to be provided as application/x-www-form-urlencoded.
For the v1 endpoint you provide (line breaks for readability only):
grant_type=authorization_code
&client_id={client-id}
&code={authoization-code}
&redirect_uri={redirect-uri}
&client_secret={client-secret}
&resource={resource-uri}
The v2 endpoint is almost identical but uses scope instead of resource:
grant_type=authorization_code
&client_id={client-id}
&code={authoization-code}
&redirect_uri={redirect-uri}
&client_secret={client-secret}
&scope={scopes}
OP's edit
Now, back to Spring Security. Spring by default uses an HTTP basic authentication scheme against Azure AD. In that scheme, the client ID and secret are encoded into the HTTP Authorization header, then the form only contains the authorization code and state parameter, so here is why I (the OP, ndr) was puzzled about why AAD refused the authorization.
In order to pass client ID and secret into the form, we can tell Spring Security to use a different supported authentication scheme. The form authentication scheme will push the client ID and secret into the form.
The below code works and retrieves the access token.
<oauth2:resource
id="msAdAuthenticationSource"
client-id="${oauth.appId}"
client-secret="${oauth.appSecret}"
type="authorization_code"
authentication-scheme="form"
client-authentication-scheme="form"
use-current-uri="true"
user-authorization-uri="${oauth.authorizationUri}"
access-token-uri="${oauth.accessTokenUri}"
scope="${oauth.scopes}"
pre-established-redirect-uri="${oauth.redirectUri}" />
Please note the two
authentication-scheme="form"
client-authentication-scheme="form"
Problem solved, a lot more to come!

ACS30001: Unable to verify the OpenID response signature

I am working on a proof of concept using Azure Active Directory Access Control Fig. 4 at The fundamentals of Azure identity management is the model I am shooting for. Since I need manage my own identifies in a deep heritage SaaS solution, I am setting up my own OpenID provider. For that, I am using DotNotOpenAuth. In my very vanilla, "hello world" example, I built a simple MVC app and registered it in my Access Control Service Namespace as a Relying Party Application and also registered the OpenIdProviderMvc project as-is from DotNetOpenAuth.Samples as my OpenID Provider using ACS Management Service. Here is the view I registered,
var openIdAddress = new IdentityProviderAddress
{
Address = "...localhost...",
EndpointType = "SignIn"
};
svc.AddRelatedObject(openId, "IdentityProviderAddresses", openIdAddress);
svc.SaveChanges();
which is the OpenID Provider endpoint page expecting to receive OpenID authentication messages to allow users to log into other web sites. This works well, when I launch my relying party application, where I installed the appropriate Nuget packages for DotNetOpenAuth (core, relying party, and their dependencies), the OpenID Providers gets called from the configuration in ACS and log in is successful. However, on redirection to ACS sending to my namespace the OpenID response, (...accesscontrol.windows.net/v2/openid?...) I get the error response:
An error occurred while processing your request.
HTTP Error Code: 502
Message: ACS30000: There was an error processing a sign-in response sent to the OpenID endpoint.
Inner Message: ACS30001: Unable to verify the OpenID response signature.
Inner Message: ACS90005: External server error.
Trace ID: 41338728-fd6e-4299-9efb-ad8684976aae
Timestamp: 2015-08-10 19:18:28Z
I am trying to figure our what I need to do to help ACS be able to verify the OpenID response signature. The response looks good (formatted for legibility and so I don't exceed my link restriction for low reputation):
https://....accesscontrol.windows.net/v2/openid?
context=cH...2
openid.claimed_id=.../user/bob
openid.identity=.../user/bob
openid.sig=NU...Rs=
openid.signed=claimed_id,identity,assoc_handle,op_endpoint,return_to,response_nonce,ns.alias3,alias3.mode,alias3.type.alias1,alias3.value.alias1,alias3.type.alias2,alias3.value.alias2,ns.sreg,sreg.email,sreg.fullname
openid.assoc_handle=WWcF!...
openid.alias3.type.alias2=.../namePerson&openid.alias3.value.alias2=bob
openid.ns.sreg=...openid.net/extensions/sreg/1.1
openid.sreg.email=bob#dotnetopenauth.net
openid.sreg.fullname=bob
Is this something I should be able to handle in the Rule Groups? Is there something I am missing in the security between my OpenId provider and ACS, such as sending some information back relating to signing algorithm, thumbprint, or something?
Thank you

Unable to login through CAS Single Sign-on using grails

I am using a grails application that has the spring security core and spring security CAS plugins. I have setup a jasig CAS server with a database authentication. So far if I login directly through CAS with the users in the database, it works fine.
The problem I have is when I try to login from the grails application. I successfully get redirected to the CAS page, where after login, I get sent back to my grails app with an error of bad credentials.
In the tomcat logs of the CAS server, I have this trace:
2015-01-21 10:55:59,182 WARN [org.jasig.cas.authentication.PolicyBasedAuthenticationManager] - <Cannot find authentication handler that supports http://localhost:8080/myGrailsApp/secure/receptor, which suggests a configuration problem.>
Below are my settings in grails:
grails.plugin.springsecurity.cas.serverUrlPrefix = 'https://localhost:8443/cas'
grails.plugin.springsecurity.cas.loginUri = '/login'
grails.plugin.springsecurity.cas.proxyReceptorUrl = '/secure/receptor'
grails.plugin.springsecurity.cas.serviceUrl = 'http://localhost:8080/myGrailsApp/j_spring_cas_security_check'
grails.plugin.springsecurity.cas.proxyCallbackUrl = 'http://localhost:8080/myGrailsApp/secure/receptor'
This is an existing project with a valid spring security that we've been using for a while, now trying to move it into single sign-on.
What setting am I missing in my CAS server or grails application? I have followed the default setup for database auth handler in setting up my CAS server.
Thanks for your help!
Do you really need to use the CAS proxy feature? Because the CAS error says that the CAS server is not configured to handle proxy callbacks while your CAS client is configured to use proxy (I guess: proxyReceptorUrl and proxyCallbackUrl properties)

Spring Security and Google OpenID Connect migration

Questions:
1) What's the best way to integrate OpenID Connect authentication into a webapp that uses Spring Security for authentication?
2) Is there any way - either from the MITREid side of things or the Google Accounts side of things - to get the MITREid OpenID Connect authentication filter to work with Google's OpenID Connect service?
I'm sure answers to these questions will be useful for any developer that uses the Spring Security OpenID module to authenticate with Google.
Detail:
My webapp uses Spring Security's OpenID module (<openid-login .../>) for authentication with Google Accounts as the Identity Provider. ie., users authenticate using their Google Apps or GMail email address.
Recently, whenever users authenticate, they receive this warning message from Google accounts:
Important notice: OpenID2 for Google accounts is going away on April
20, 2015.
So Google is dropping support for OpenID, will turn it off completely in April 2015, and states that you must switch to the OpenID Connect protocol if you want to authenticate with Google Accounts.
I was hoping Spring Security would have built-in support for OpenID Connect, just like it has built-in support for OpenID. e.g. something like an <openid-connect-login .../> element. But my searches have turned up no such support.
The best candidate I've found so far is MITREid Connect . It includes a Spring Security authentication filter named OIDCAuthenticationFilter for OpenID Connect. The problem is, it does not interoperate with Google's OpenID Connect implementation.
I tried cloning the MITREid simple-web-app and configured it to authenticate (using OpenID Connect) with Google Accounts. But it did not work because it depends on a nonce which Google's OpenID Connect implementation does not support. The error message from Google accounts was:
Parameter not allowed for this message type: nonce
Next I tried plugging my own implementation of MITREid's AuthRequestUrlBuilder interface into the MITREid configuration. The only difference between my implementation and MITREid's implementation was that I did not send the nonce.
Not sending the nonce made Google's OpenID Connect implementation happy but MITREid threw an exception when it couldn't find a nonce in the Google authentication response. The error message was:
Authentication Failed: ID token did not contain a nonce claim
I tracked the MITREid exception down to these lines in MITREID'S OIDCAuthenticationFilter:
// compare the nonce to our stored claim
String nonce = idClaims.getStringClaim("nonce");
if (Strings.isNullOrEmpty(nonce)) {
logger.error("ID token did not contain a nonce claim.");
throw new AuthenticationServiceException("ID token did not contain a nonce claim.");
}
But there is no way for me to extend MITREid's implementation to ignore the nonce. So close but yet so far! If Google Accounts would accept the nonce or MITREid could be configured to ignore the nonce then we'd have a solution.
Within the MITREid Connect issues list on github I've found others have run into these similar issues:
1) #726 - Documentation on using client with Google as authentication provider
2) #704 - Add a useNonce attribute into ServerConfiguration to indicate if the IdP accepts the nonce value into its requests.
So I am stuck. Come April 2015 Google will shutdown Open ID authentication.
Some relevant links:
1) https://support.google.com/accounts/answer/6135882
2) https://www.tbray.org/ongoing/When/201x/2014/03/01/OpenID-Connect
3) https://github.com/mitreid-connect
4) https://github.com/mitreid-connect/OpenID-Connect-Java-Spring-Server/blob/master/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java
5) https://github.com/mitreid-connect/simple-web-app
6) https://github.com/mitreid-connect/OpenID-Connect-Java-Spring-Server/blob/master/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/PlainAuthRequestUrlBuilder.java
7) https://github.com/mitreid-connect/OpenID-Connect-Java-Spring-Server/issues/726
8) https://github.com/mitreid-connect/OpenID-Connect-Java-Spring-Server/pull/704
2015-02-18 Update
Functionality has recently been added to the development branch of mitreid-connect for disabling the nonce - therefore making Google's OIDC server happy. Thankfully, mitreid-connect has also provided some guidance on interoperating with Google .
Unfortunately the "nonceEnabled" change is not yet available in Maven central but hopefully that will change soon.
AFAIK, there is no clean and easy Spring Security migration from OpenID to OpenID Connect authentication. Implementing OpenID authentication with Spring Security is straight-forward using the well documented <openid-login/> but there exists no analog for OpenID Connect.
The MITREid alternative is still on a development branch and unavailable at Maven Central and therefore not a candidate.
In the comments, Chuck Mah points to How to implement Openid connect and Spring Security where Romain F. provides the sample code.
Romain's sample code pointed me in the right direction. Given time is running out, I went with romain's approach, which was to write a custom Spring Security AuthenticationFilter that uses spring-security-oauth2 to query the oauth2 api userinfo endpoint (for Google that's https://www.googleapis.com/oauth2/v2/userinfo). The assumption is that if we are able to successfully query the userinfo endpoint then the user has successfully authenticated so we can trust the information returned - eg the user's email address.
When i first started learning about OpenID Connect the “id token” seemed to be the central concept. However, browsing the spring-security-oauth2 source code, it appears to be ignored. This leads to the question, what’s the point of the ID token if we can authenticate without it (by simply querying oauth2 userinfo endpoint)?
A minimalist solution - which i would prefer - would simply return a validated ID token. There would be no need to query the userinfo endpoint. But no such solution exists in the form of a Spring Security authentication filter.
My webapp was not a spring-boot app like romain's. spring-boot does alot of configuration behind the scenes. Here are some of the problems/solutions I encountered along the way:
problem: HTTP Status 403 - Expected CSRF token not found. Has your session expired?
solution: java config: httpSecurity.csrf().disable()
problem: HTTP Status 500 - Error creating bean with name 'scopedTarget.googleOAuth2RestTemplate': Scope 'session' is not active for the current thread;
solution: java config: OAuth2RestTemplate does not need to be session scoped (OAuth2ClientContext is already session scoped and that's all that's necessary)
problem: HTTP Status 500 - Error creating bean with name 'scopedTarget.oauth2ClientContext': Scope 'session' is not active for the current thread;
solution: web.xml: add RequestContextListener
explanation: because the oauth2ClientContext session-scoped bean is accessed outside the scope of the Spring MVC DispatcherServlet (it is being accessed from OpenIdConnectAuthenticationFilter, which is part of the Spring Security filter chain).
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
problem: org.springframework.security.oauth2.client.resource.UserRedirectRequiredException: A redirect is required to get the users approval.
solution: web.xml: Add filter definition immediately PRECEEDING springSecurityFilterChain
<filter>
<filter-name>oauth2ClientContextFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>oauth2ClientContextFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Unfortunately, OpenID Connect does not allow us to request only email scope.
When our users authenticated using OpenID they would see a consent screen like "webapp would like to view your email address" with which they were comfortable. Now we must request scopes openid email resulting in a consent screen asking the user to share their entire public profile with us ... which we really don't need or want ... and users are less comfortable with this consent screen.

Resources