Implementing a SAML assertion flow with Spring Oauth2 - spring-security-oauth2

I need to implement a SAML assertion flow using Spring OAuth2 Authorization Server. I'm struggling with how to tie these two projects up.
I've tried importing the SAML securityContext into my OAuth project as well, however it seems that the SAML flow will always take precedence. Is there a way to do this where it would look at the grant_type to determine if it should use the saml flow.
Also, when it does process the saml assertion, would that tie into the ResourceOwnerPasswordTokenGranter, or would I have to register a custom token granter with something like this?
With OAuth2, we have a way of registering different TokenGranters through something like this:
List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>();
tokenGranters.add( endpoints.getTokenGranter());
tokenGranters.add(new SamlTokenGranter(authenticationManager,tks,cds,endpoints.getOAuth2RequestFactory()));
CompositeTokenGranter ctg = new CompositeTokenGranter(tokenGranters);
endpoints.tokenGranter(ctg);
thanks ahead of time for any help you can provide

You might need to register a custom TokenGranter. The CompositeTokenGranter (the default) can handle multiple grant types by doing what you suggest (comparing the incoming requested grant_type to the one supported by each granter). If you are using Spring SAML as a service provider to check the assertion, then I guess you also need to set that up as a separate security filter on the /token endpoint (with a very specific request matcher so it only intercepts SAML grants).

Related

How to use end client state parameter in IdentityServer 3?

I have configured IdentityServer 3 to use external IdentityProvider which is pointing to AAD.
As of now, when I send a request to IdentityServer, I am properly redirected to the AAD for login, however, the 'state' parameter that I am sending to IdentityServer is overridden, and the value of OpenIdConnect.AuthenticationProperties is encrypted and sent to the AAD as the state in the query string.
For eg:
https://localhost:44333/idpaad/connect/authorize?client_id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&redirect_uri=https://localhost:44394/&response_mode=query&response_type=code&scope=openid%20email&state=9b0e82c3-e623-42f1-bede-493243c103e7
Here,
https://localhost:44333/idpaad/connect/authorize -> IdentityServer endpoint
state=9b0e82c3-e623-42f1-bede-493243c103e7 -> client generated GUID sent as querystring.
when I see in the "RedirectToIdentityProvider" middleware in the StartUp.cs of IdentityServer OpenIdConnectAuthenticationNotifications, the value of state is updated to
OpenIdConnect.AuthenticationProperties=(protected values) instead of the GUID and the same is also returned as a query string back to the Redirect URI.
enter image description here
Is there a way to send the original state and not override it by IdentityServer3?
While using wsFederation, I am not getting this issue and the same is forwarded directly to the IdP.
Any help is deeply appreciated.
Most of the time it's advisable for an Azure Active Directory integrated application to maintain an application state when sending request to Azure AD for login. And the recommended way to achieve this is to use the ‘state’ parameter as defined in the OpenID Connect standards.
If you check this document form OpenID, you will find that primary reason for using the state parameter is to mitigate CSRF attacks.
RECOMMENDED. Opaque value used to maintain state between the request and the callback. Typically, Cross-Site Request Forgery (CSRF, XSRF) mitigation is done by cryptographically binding the value of this parameter with a browser cookie.
The ‘state’ parameter is used for both preventing cross-site request forgery attacks and to maintain user’s state before authentication request occurs.
In an ASP.NET or ASP.NET CORE web application using OpenID Connect OWIN middleware, the ‘state’ parameter is maintained automatically by the middleware when sending out an authentication request, this is the only reason you are seeing the state parameter getting overridden in your case.
But if you want you can add custom data in your state parameter. Use the following code in OpenIdConnectNotifications’s RedirectToIdentityProvider event to inject custom data into the ‘state’ parameter.
var stateQueryString = notification.ProtocolMessage.State.Split('=');
var protectedState = stateQueryString[1];
var state = notification.Options.StateDataFormat.Unprotect(protectedState);
state.Dictionary.Add("MyData","123");
notification.ProtocolMessage.State = stateQueryString[0] + "=" + notification.Options.StateDataFormat.Protect(state);
Check this document and Microsoft identity platform and OpenID Connect protocol for detailed information.

Endpoint authorization for service to service call

I have a .net core API with multiple endpoint. I am using Identity Server 4 for authentication. Token will be generated based on correct user name and password.
In the Same API, I want to add another endpoint which should only be accessed by API call only and normal user should not be able to access the end point.
I am thinking of Policy based authorization for the endpoint.
Is it a correct approach?
There's a couple things you could do.
As you mention you can use authorization policies. If you're using OAuth; your source API can use client credential grant type to obtain a M2M token (a.k.a. machine-to-machine token). In the target service can do a check the gty claim is equal to client_credentials, and if not; return HTTP 403 response.
Use the API gateway and only expose public APIs/operations on internal services - don't expose public routes for APIs/operations that should be restricted to service-to-service calls.
Do both #1 and #2.
Yes, you need to configure your policies. For that you need to understand the difference between authentication and authorization, and setup them both. For example in our project we use several different authentication sources, and then map them to authorization policies
services.AddAuthentication()
.AddScheme<...>("scheme1", ...)
.AddScheme<...>("scheme2", ...)
...;
services.AddAuthorization(opts =>
{
opts.AddPolicy("policy1", policy => policy.AddAuthenticationSchemes("scheme1")
.<some other requirements specific to this policy>
.RequireAuthenticatedUser());
opts.AddPolicy("policy2", policy => policy.AddAuthenticationSchemes("scheme2")
.<some other requirements specific to this policy>
.RequireAuthenticatedUser());
}
After which you could just use [Authorize("policy1")] and [Authorize("policy2")]. Or you could make one policy the default by adding
opts.DefaultPolicy = new Microsoft.AspNetCore.Authorization
.AuthorizationPolicyBuilder()
.<requirements go here>
and then use [Authorize] to use the default policy, and [Authorize("policy1")] for special cases.

Spring-Security-OAuth2 - how to add fields to access token request?

I have a Spring Boot application, that is using Spring Security with OAuth 2.0. Currently, it is operating against an Authentication Server based on Spring Example code. However, running our own Auth Server has always been a short-term target to facilitate development, not a long-term goal. We have been using the authorization_code grant type and would like to continue using that, irrespective of the Auth Server implementation.
I am attempting to make changes to use OAuth 2.0 Endpoints in Azure Active Directory, to behave as our Authentication Server. So far, I have a successful call to the /authorize endpoint. But the call to get the /token fails with an invalid request error. I can see the requests going out.
It appears that parameters that Azure states as mandatory are not being populated in the POST request. Looking at the Azure doco, it expects the client_id to be defined in the body of the message posted to the endpoint, and that is not added, by default, by Spring.
Can anyone point me in the right direction for how I can add fields to the Form Map that is used when constructing the Access Token request? I can see where the AccessTokenRequest object is being setup in OAuth2ClientConfiguration....
#Bean
#Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
protected AccessTokenRequest accessTokenRequest(#Value("#{request.parameterMap}")
Map<String, String[]> parameters, #Value("#{request.getAttribute('currentUri')}")
String currentUri) {
DefaultAccessTokenRequest request = new DefaultAccessTokenRequest(parameters);
request.setCurrentUri(currentUri);
return request;
}
Should I be trying to define the map in a request.parameterMap spring property? If so, I'm not too sure how that works.
Or should I be using one of the interfaces defined in the AuthorizationServerConfigurerAdapter class?
I have the information to include when sending the AccessTokenRequest, I just don't know the best way to configure Spring to include it? Thanks for any help.
Actually, I found this out. I needed to change the client authentication scheme. Simply adding the following to my application properties added the client_id to the form....
security.oauth2.client.clientAuthenticationScheme=form
If you're using yaml, then yaml-ize it. Thank you Spring!

#WithMockUser ignores username, password fields?

I'm using #WithMockUser to test a controller method secured with basic auth in a simple spring boot web service. My annotation looks like:
#WithMockUser(username = 'admin', password = 'mypassword', roles = ['ADMIN'])
It seems however that the username and password field are ignored. The test passes even if I specify an incorrect username or password. The roles field however does appear to be used and the test fails with an invalid role is provided.
Perhaps I'm misunderstanding the role of this annotation, but should I be able to use it to verify behavior of incorrect credentials being supplied to a secured method?
Thanks!
--john
If you are using Spring MVC Test (i.e., MockMvc), you would do something like the following:
mvc.perform(get("/").with(httpBasic("user","password"))) // ...
This is documented in the Testing HTTP Basic Authentication section of the Spring Security reference manual.
If you have configured your test to launch Spring Boot with an embedded Servlet container, you can use Spring Boot's TestRestTemplate which supports "Basic HTTP authentication (with a username and password)".
This has happened to me as well. I think #WithMockUser is enough to test secured services. user/password/roles is not required. I validated this scenario in my case.

How to created signed AuthNRequest?

I am interfacing with an IDP and have a basic AuthNRequest created as follows:
<samlp:AuthnRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="IDTest1"
Version="2.0"
IssueInstant="2013-03-04T09:21:59"
AssertionConsumerServiceIndex="0"
AttributeConsumingServiceIndex="0">
<saml:Issuer>https://myapp.com/saml2/sp</saml:Issuer>
<samlp:NameIDPolicy
AllowCreate="true"
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"/>
</samlp:AuthnRequest>
IDP wants me send the request as signed. My questions are:
How do I set digest value?
How do I set Signature value?
For x509 certificate, I set the public key of my app. Correct?
What is the data that is used to compute any of the values? Is it my original auth request without Signature element?
Just to note that a lot of this is covered in the documentation:
SAML metadata.
To have the request signed you need to add something like this (normally found in the sp.xml):
<SPSSODescriptor AuthnRequestsSigned="true" WantAssertionsSigned="false"
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
The signing key would look something like:
<KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>
MIIDWTC...CAkGgAwIBAgIEe+a+/uaSZCp5g2z+hRWRV+DyfQc9nO
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</KeyDescriptor>
where the MII... is the public key.
As per #Stefan, it's much easier to use a library.
SAML Authentication Request is an XML document. You can sign SAML Authentication Request just like signing any other XML document. There are, however, some restrictions:
The signature must be enveloped signature.
Before it is digested, the SAML Authentication Request must not be transformed by method other than enveloped signature transform and exclusive canonicalization transform.
The Signature element must contain only one Reference element.
The URI of the only Reference element must contain the value of the ID attribute of the signed SAML Authentication Request.
Before it is signed, the SignedInfo element must be canonicalize using exclusive canonicalization method.
You can read more detail the SAML Assertions and Protocols Specification (http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf) in Section 5.
Your question is inadequate!
The AuthRequest you're sending seems to be REDIRECT request where you will not see Digest, Signature and Certificate since all these details will be in URL as a parameter.
Try using POST SSO request, where you will see Digest, Signature and Certificate in SAML request.
Some of the points:
Common
Both IdP and SP should share their Metadata, which will have their basic configuration like id, signing algorithm, hashing method, public key etc.
So, based on the contract between IdP you should hash and sign your request in your preferred programming language.
SP:
You should encrypt using your public key.
You should sign using your private key.
You should encode your request using Base64.
IdP:
They will identity using the public key in the request.
They will respond back with encrypted and signed XML.
You should decrypt and unsign the response.
Quick Links
Official Doc about SAML 2.0
SAML Online Tool by OneLogin
If your into constructing your own requests without any bigger frameworks around I can recommend OpenSAML. Its a library to help with the construction of SAML messages.
In my book, A Guide to OpenSAML, this and more is explained in detail.
EDIT I have released a new edition of the book, covering OpenSAML V3
Here is an example I wrote on signing SAML messages
And one on how to dispatch AuthnRequests.
Well things concerning security are never easy...
you sholud definetly check documentation Linked by #nzpcmad, as well as
SAML2 profiles (look for WB SSO - Web Browser Single Sign On).
For Java OpenSaml is indeed one of easiest solutions.
A pitfall seems to be that with HTTP-Redirect Binding the Signature is transported by additional URL-Parameters and not part of the SAMLRequest value, e.g. https://my-idp.com/login?SAMLRequest=nVNN...%3D&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256&Signature=QZ64...%3D%3D

Resources