Endpoint authorization for service to service call - asp.net

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.

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.

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!

Authorization by Role/Group in ASP.NET using Auth0

Thanks in advance for your help in this matter!
I was hoping someone could help me figure out how to authorize API access by Group assigned in the Auth0 Authorization extension.
I currently am using the [Authorize] attribute in the web api perfectly - it allows an api call if they have signed in successfully and blocks it if not.
However, if I try [Authorize(Roles = "myGroupName")] authorization fails. Same occurs if I add it to the users app_metadata manually in the Users dashboard on the Auth0 website instead of assigning through the extension.
My project is set up by following the Angular Quick Start and Asp.Net Quick Start. My webapiconfig where I validate the token server side is:
class WebApiConfig
{
public static void Register(HttpConfiguration configuration)
{
var clientID = WebConfigurationManager.AppSettings["auth0:ClientId"];
var clientSecret = WebConfigurationManager.AppSettings["auth0:ClientSecret"];
configuration.MessageHandlers.Add(new JsonWebTokenValidationHandler()
{
Audience = clientID,
SymmetricKey = clientSecret
});
configuration.Routes.MapHttpRoute("API Default", "api/{controller}/{id}",
new { id = RouteParameter.Optional });
}
}
The Auth0 Authorization extension currently supports authorization decisions through the concept of groups. You can create a group, assign users to that group and that configure an application to only be accessible to user within a specific group. All of this would be handled automatically and any user outside of the application expected groups would be denied complete access.
Your use case is a bit different, but valid nonetheless. You want the groups configured with the extension to be sent along the generated token so that the application itself makes authorization decisions based on those values.
In order for the groups configured within the extension to be sent along in the token, the first thing you need to do is request them. For this, you need to include the groups scope when performing the authentication requests.
Add the user's group membership to the outgoing token (which can be requested via the OpenID groups scope);
(emphasis is mine, source: Authorization Extension Docs, section Rule Behavior)
If you request a token using that scope and then decode it in jwt.io, you would get something similar to this (the actual groups would vary by user):
{
"groups": [
"GROUP-1",
"GROUP-2"
],
"iss": "https://[tenant].auth0.com/"
}
Now, for the validation of this information on the ASP .NET API side. Assuming the sample you're using is this one (ASP.NET Web API), the group information contained within the token would be mapped to the following claims:
Type: groups | Value: GROUP-1
Type: groups | Value: GROUP-2
This happens because of the logic that exists in the JsonWebToken class which handles arrays coming from the JWT payload by creating per-value claim that share the same type.
The final part is making sure the AuthorizeAttribute checks these claims of type groups instead of trying to lookup role claims. You should be able to accomplish this, by changing the RoleClaimType constant in the JsonWebToken class to have the value "groups" instead of "http://schemas.microsoft.com/ws/2008/06/identity/claims/role".
Like you certrainly know, the Authorize attribute works using what is in the principal: something that inherits IPrincipal.
In web api, it is even more specific; it is something that inherits ClaimsPrincipal (this implements himself IPrincipal).
As you certainly know already, a claim is like a key-value pair.
The ClaimsPrincipal contains a serie of key-value pairs that are directly taken from the authentication token. This authentication token is issued by the authentication server most of time as JWT (Json Web Token). Most of time as well, the authentication server is using OAuth, like is your case.
If the user group, that you expect to be the role in your application doesn't work by using the out-of-the-box Authorize attribute, it's because it is not mapped correctly: Auhtorize checks the claim with claim type: http://schemas.microsoft.com/ws/2008/06/identity/claims/role (the "claim type" is the "key" of the key-value pair). That means that if you want your Authorize to work, this claim must be valued with the group.
You can do several things to have a clean authorization in your application.
Make a custom Authorize attribute. This Authorize attribute would check the role using a different claim type. The claim type that refers to the user group depends on your authentication server. If you don't find what claim type is used for groups in the doc of your authentication server, run your application in debug, and check every claim that is contained in the property User of your controller. You will certainly find what the claim type you are interested in.
Change the setup of your authorization server by redefining the mapping between user information and claims of the token that is produced (in your case, map groups of the user to the claim that has the type http://schemas.microsoft.com/ws/2008/06/identity/claims/role). Generally, this can be setup per client application or even globally. For example this is the way that must be done if you use an ADFS authentication, AzureAD or WSO2 authentication server (http://wso2.com/products/identity-server/)
Add an owin middleware to modify the current principal. It will change the current principal by copying the value of the claim that contains groups into the claim type http://schemas.microsoft.com/ws/2008/06/identity/claims/role. This middleware must be inserted in the flow after the authentication middleware
I have no rights to comment so I'm going to inquire from here. Why are you doing this
[Authorize(Roles = "myGroupName")]
as far as I remember when I was implementing group based authorization I was still typing
[Authorize(Roles = "myRoleName")]
Not other way around.

Implementing a SAML assertion flow with Spring 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).

Custom HTTP Basic Authentication for ASP.NET Web Services on .NET 3.5/VS 2008

I am refactoring a working ASP.NET Web Application to expose Web Services interface using ASP.NET Web Service. According to Web Services authentication - best practices, Basic Auth over https is the way to go. Let's assume it is, as opposed to doing WS-Security, X509, etc..
On .NET 3.5/VS 2008, what's the simplest way of implementing custom http Basic Authentication (non-Windows account), for example, accepting only if user name is "foo" and password is "bar". Ultimately, I'd like Thread.CurrentPrincipal set.
Do I write my own HttpModule or can this be done simpler?
Likely using Custom Basic Authentication for IIS, written by Dominick Baier is the way to go. As he points out WCF 3.5's usernames over transport security cannot be used on IIS-hosted service, although my question was regarding ASP.NET Web Services not WCF.
There's another implementation of HTTP Module called Basic authentication in ASP.NET against custom datasource by Santosh Sahoo.
Although it's not what I wanted, I found QuickStart Tutorial's SOAP Headers sample to be informative workaround. Sending password in plain text over http is clearly insecure, but this example could be extended to add more security, for instance running on https or sending hash of "password + one-time GUID + timestamp".
Grab the value of the Authorization header, parse it and validate the username/password.
The value is username:password, encoded as a Base64 string.
See http://en.wikipedia.org/wiki/Basic_access_authentication for details.
Edit: if you want this done for every request, using the custom auth scheme, then it would be easier to write an HttpModule to handle parsing the header and setting the thread's principal.
If you are considering WCF, you can use usernameOverTransport security for basicHttpBinding. i.e. username and passowrd reside in the SOAP header of the request and all the traffic are protected by SSL encryption over the wire.
A custom UserNamePasswordValidator validator can be used to authenticate the incoming credentials against e.g. database.
You can set the thread principal within a custom IAuthorizationPolicy in the service behavior.
e.g. Evaluate method implementation for IAuthorizationPolicy for setting current principal
public bool Evaluate(EvaluationContext evaluationContext, ref object state)
{
Object obj;
if( evaluationContext.Properties.TryGetValue( "Identities", out obj ))
{
// get the authenticated identity
IIdentity client = (obj as IList<IIdentity>)[0];
evaluationContext.Properties["Principal"] = ... // create principal obj here for the identity
// this will set thread's current principal
}
return true;
}

Resources