Role based authorization in asp.net web api using ad access token - asp.net-web-api

I need to implement role based authorization for asp .net web api
from native client application the access token will be sent in the header ,
so when i give [Authorize] attribute to my web api it is working fine,but when i give [Authorize(Role="Admin")] it is giving unauthorized error,
then whenever check for role in the claims it is always null.
My startup.cs is
public void Configuration(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = ConfigurationManager.AppSettings["ida:Audience"],
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
});
}
and i have applied authorize attribute for a method
[Authorize]
[HttpGet]
public async Task<UserDetails> TestAuthorization()
{
string upn = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Name).Value;
string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
var role = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Role);
return null;
}
here the role value is always null
Please help me on this

Related

Authorization using JWT token in .net framework 4.7 app

I'm working with a .net framework 4.7 app hosted in IIS. The api needs to be secured with JWT token. Identity is provided by another server and clients will send the JWT as bearer token in the header. I like to use OWIN pipeline for authorization. Currently the app uses Global.asax for startup and I like to keep it as is. I just want OWIN for authorization using JWT. I will use the [Authorize] attribute on the controllers needing jwt authorization. IIS doesn't do any authorization at the moment.
I have this in the Startup.cs for Owin.
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = ConfigHelper.GetAudience(),
ValidIssuer = ConfigHelper.GetIssuer(),
ValidateLifetime = true,
ValidateIssuerSigningKey = true
}
});
}
}
How do I call the Startup.Configure() from Global.asax so Owin pipeline handles the authorization for incoming requests.
Thanks
You can have both global.asax and OWIN startup in the same project. ASP.NET will first call the global.asax and hand the control over to OWIN's startup. Make sure you have the Microsoft.Owin.Host.SystemWeb package installed in the project. And you have a class with the name Startup and a method Configuration(IAppBuilder app). There are other ways to let OWIN know where it should start.
You should also be aware of the fact that in .NET framework, there is a manual process to retrieve the signing keys from the authority that issued the JWT token. Otherwise, you will get the mismatched key error. Once you get the keys, you will assign them to ValidSigningKeys property in TokenValidationParameters. Search SO for examples.
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = ConfigHelper.GetAudience(),
ValidIssuer = ConfigHelper.GetIssuer(),
ValidateLifetime = true,
ValidateIssuerSigningKey = true
}
});
}

"Authorization has been denied for this request" when hosting Azure App Service Backend locally on IIS

In my Azure App Service .NET backend, I typically decorate my controllers with [authorize] so that they are protected against users who are not signed in. For uer authentication I set up an Azure Active Directory B2C (AADB2C) tenant, configured an application as well as some identity providers and everything works as expected as long as I am hosting my backend on Azure.
Now, I'd like to be able to run the backend locally so that I can easily debug the code and have it work against a local development database.
However, it does not work.
I went to https://[myappservice].scm.azurewebsites.net/ and retrieved the WEBSITE_AUTH_SIGNING_KEY from the environment tab.
I added the key to my backends' web.config as Signingkey under AppSettings
I changed ValidAudience and ValidIssuer to the respective values (https://[myappservice].azurewebsites.net)
So that I can bind the backend to an IP address or my hosts name rather than "localhost" (which would be the case with IIS Express), I configured the backend to run under IIS
I only allowed anonymous access on my IIS Website and let it run under the DefaultAppPool which uses the integrated pipeline on the 4.0 CLR
My client code does not use MobileServiceClient but MSAL (PublicClientApplication). So the documentation about how to setup a local dev environment found here does not apply.
Because I have a RequestProvider class that creates the necessary HttpClient and adds the access token to the header, this shouldn't be a problem, because for authentication, the PublicClientApplication object uses the AADB2C tenant and the respective server flow policies for signing in and -up and for calling the backends' rest controller I can switch between the Azure hosted and the locally hosted backend.
However, while authentication keeps working like a charm and I am able to receive a working access token, the backend that is hosted locally does not accept it while the same backend code hosted on azure does.
So I started searching the net and found out about the website jwt.io. I pasted my access token into their decoder and was astonished about the result.
Some article mentioned that the values ValidIssuer and ValidAudience configured in the backends' web.config need to exactly reflect the values iss and aud contained within the access token.
The values in my access token however don't match the scheme which was part of the web.config template though (https://[myappservice].azurewebsites.net).
The iss field in my decoded token looked like this:
https://[myappservice].b2clogin.com/12345678-1234-1234-1234-123456789012/v2.0/
The aud field in my decoded token looked like this:
12345678-1234-1234-1234-123456789012
The isser value can be found in my AADb2C tenants' directory within the signin/-up policies' settings under Properties in "Token compatibility settings" as Issuer (iss) claim. Theres a combobox from which the following value is selected:
https://<domain>/12345678-1234-1234-1234-123456789012/v2.0/
The aud fields' guid equals the Client ID configured in the App Service on Azure.
However, even if I take these two values and enter them into the web.config as described above, I still get an "Authorization has been denied for this request" error.
I really don't know how to get this working...
I tried to reproduce your issue and it works well if in my environment. I was referring to active-directory-b2c-dotnet-desktop as native client and active-directory-b2c-dotnet-webapp-and-webapi (host the api project on IIS, web app project is not used) as backend api.
I also encountered the same issue several times when testing, and resolved it by the following steps:
No special configuration is required on IIS side. You don't need to specify "Signingkey" in web.congig because when validating the token, signing key will be fetched automatically from metedata endpoint (jwks_uri field):
https://TenantName.b2clogin.com/TenantName.onmicrosoft.com/v2.0/.well-known/openid-configuration?p=SignInPolicyName
In Azure Portal, make sure you have granted native client the access to api app.
For other basic configuration, refer to the instruction of samples above.
I also suggest you decode the access token and confirm the primary claims are correct:
"iss" should be same as the AAD instance in API app
"aud" should be client id
"sub" or "oid" should be the object id of the logged in user.
"tfp" should be the sign in policy name
"scp" should be published scope of the API app
For other claims, please refer to Azure AD B2C: Token reference
Note, the official samples above are still using login.microsoftonline.com as authority endpoint, intsead of b2clogin.com. To use b2clogin, just update the endpoint accordingly.
The below is core code snippet and settings in my working sample:
Native client
public partial class App : Application
{
private static string TenantName = "<TenantName>";
private static string Tenant = "<TenantName>.onmicrosoft.com";
private static string ClientId = "<ClientID>";
public static string PolicySignUpSignIn = "B2C_1_signupsignin1";
public static string PolicyEditProfile = "b2c_1_edit";
public static string PolicyResetPassword = "b2c_1_reset";
public static string[] ApiScopes = { "https://<TenantName>.onmicrosoft.com/demoapi/demo.read" };
public static string ApiEndpoint = "http://<IISServerAddress>/taskservice/api/tasks";
private static string BaseAuthority = "https://login.microsoftonline.com/tfp/{tenant}/{policy}/oauth2/v2.0/authorize";
private static string BaseAuthorityForb2clogin = "https://{TenantName}.b2clogin.com/tfp/{tenant}/{policy}";
public static string Authority = BaseAuthorityForb2clogin.Replace("{tenant}", Tenant).Replace("{policy}", PolicySignUpSignIn).Replace("{TenantName}", TenantName);
public static PublicClientApplication PublicClientApp { get; } = new PublicClientApplication(ClientId, Authority, TokenCacheHelper.GetUserCache());
}
API app config:
<add key="ida:AadInstance" value="https://tomtestb2c.b2clogin.com/{0}/v2.0/.well-known/openid-configuration?p={1}" />
<add key="ida:Tenant" value="TenantName.onmicrosoft.com" />
<add key="ida:ClientId" value="ClientId-ff8e-4ac2-a093-6c6e0f8e116c" />
<add key="ida:SignUpSignInPolicyId" value="B2C_1_signupsignin1" />
<!-- The following settings is used for requesting access tokens -->
<add key="api:ReadScope" value="read" />
<add key="api:WriteScope" value="write" />
Auth code:
public partial class Startup
{
// These values are pulled from web.config
public static string AadInstance = ConfigurationManager.AppSettings["ida:AadInstance"];
public static string Tenant = ConfigurationManager.AppSettings["ida:Tenant"];
public static string ClientId = ConfigurationManager.AppSettings["ida:ClientId"];
public static string SignUpSignInPolicy = ConfigurationManager.AppSettings["ida:SignUpSignInPolicyId"];
public static string DefaultPolicy = SignUpSignInPolicy;
/*
* Configure the authorization OWIN middleware
*/
public void ConfigureAuth(IAppBuilder app)
{
TokenValidationParameters tvps = new TokenValidationParameters
{
// Accept only those tokens where the audience of the token is equal to the client ID of this app
ValidAudience = ClientId,
AuthenticationType = Startup.DefaultPolicy
};
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
// This SecurityTokenProvider fetches the Azure AD B2C metadata & signing keys from the OpenIDConnect metadata endpoint
AccessTokenFormat = new JwtFormat(tvps, new OpenIdConnectCachingSecurityTokenProvider(String.Format(AadInstance, Tenant, DefaultPolicy)))
});
}
}

How to validate facebook authorization token and sign up user using Spring(java)

I am developing an app whose frontend is written using React.js and the backend REST API is written using the Spring framework. I wanted to add social logins to my website, so after days of googling and research, I understood that OAuth2 is the solution. I came to know that the frontend should handle getting the authorization token from the Resource Server(Facebook here) and my backend(java) should validate that token and connect with Facebook to get an access token. Then that access token should be stored in my database along with the user details(e.g email).
Here is my requirement, once the user clicks on the "Continue with Facebook" button, my app should create there account in my own database using details - email and Name(the signup feature). And later whenever they click on this button again, they will be logged in not sign up. The way other websites handle it.
As of now, I have the button working in my app, which brings me the authorization token from Facebook.
Can someone please guide me the path I should follow here.
Also, any special attention to some error handling I should follow.
Here's the general approach using Spring Boot as a REST API backed by Spring Data JPA and Spring Security that works for iOS and ember.js together. There's probably libraries and what not that you can use but I'm just going to outline the fundamental flow.
Your user object needs a one to one mapping to a facebook account. Best practice would involve encrypting the authToken before storing in the DB
#Entity
class FacebookAccount {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
Long id
String facebookUserId
String authToken
#OneToOne
#JoinColumn(name="user_id")
User user
}
#Entity
class User{
...
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
FacebookAccount facebookAccount
}
Use the facebook Javascript SDK to get a User Access Token and the User's Facebook User ID. You'll get a response back from facebook in your react app that looks like this in the successful case:
{
status: 'connected',
authResponse: {
accessToken: '...',
expiresIn:'...',
reauthorize_required_in:'...'
signedRequest:'...',
userID:'...'
}
}
Hit some login endpoint with the info received in step 2 like /login/facebook. I cannot predict how your app is structured. In my app, this code is handled by my Authentication Filter that implements GenericFilterBean. I pass a header X-Auth-Facebook with the token.
Verify the token. I'm doing this in a class that implements AuthenticationProvider within the Authentication authenticate(Authentication authentication) throws AuthenticationException method. This class will need your App's Access Token accessToken and the user's Token userAccessToken:
URIBuilder builder = URIBuilder.fromUri(String.format("%s/debug_token", "https://graph.facebook.com"))
builder.queryParam("access_token", accessToken)
builder.queryParam("input_token", userAccessToken)
URI uri = builder.build()
RestTemplate restTemplate = new RestTemplate()
JsonNode resp = null
try {
resp = restTemplate.getForObject(uri, JsonNode.class)
} catch (HttpClientErrorException e) {
throw new AuthenticationServiceException("Error requesting facebook debug_token", e)
}
Boolean isValid = resp.path("data").findValue("is_valid").asBoolean()
if (!isValid)
throw new BadCredentialsException("Token not valid")
String fbookUserId = resp.path("data").findValue("user_id").textValue()
if (!fbookUserId)
throw new AuthenticationServiceException("Unable to read user_id from facebook debug_token response")
// spring data repository that finds the FacebookAccount by facebook user id
FacebookAccount fbookAcct = facebookAccountRepository.findByFacebookUserId(fbookUserId)
if(!fbookAcct){
// create your user here
// save the facebook account as well
} else{
// update the existing users token
fbookAcct.authToken = userAccessToken
facebookAccountRepository.save(fbookAcct)
}
// finish the necessary steps in creating a valid Authentication
I, personally, then create a token that my client's use when accessing my API (rather than have them continue to pass the facebook token with all requests).
I also need more user provided information to create the user (a chosen username, agreeing to terms and conditions, etc). So my actual implementation throws an EntityNotFoundException instead of creating the user, which my clients then use to pop up a registration form that provides only the fields I cannot get from facebook. On submit of this from the client, I hit my /signup/facebook endpoint with the facebook token and what's needed to create my user. I fetch the profile from facebook and create the user (automatically logging them in the process).
Edit: If you want to use Spring 0Auth, you could follow the example for creating a Spring 2 Oauth Rest Template
#Bean
public OAuth2ProtectedResourceDetails facebook() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
details.setId("facebook");
details.setClientId("233668646673605");
details.setClientSecret("33b17e044ee6a4fa383f46ec6e28ea1d");
details.setAccessTokenUri("https://graph.facebook.com/oauth/access_token");
details.setUserAuthorizationUri("https://www.facebook.com/dialog/oauth");
details.setTokenName("oauth_token");
details.setAuthenticationScheme(AuthenticationScheme.query);
details.setClientAuthenticationScheme(AuthenticationScheme.form);
return details;
}
#Bean
public OAuth2RestTemplate facebookRestTemplate(OAuth2ClientContext clientContext) {
OAuth2RestTemplate template = new OAuth2RestTemplate(facebook(), clientContext);
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON,
MediaType.valueOf("text/javascript")));
template.setMessageConverters(Arrays.<HttpMessageConverter<?>> asList(converter));
return template;
}
and then in use:
public String photos(Model model) throws Exception {
ObjectNode result = facebookRestTemplate
.getForObject("https://graph.facebook.com/me/friends", ObjectNode.class);
ArrayNode data = (ArrayNode) result.get("data");
ArrayList<String> friends = new ArrayList<String>();
for (JsonNode dataNode : data) {
friends.add(dataNode.get("name").asText());
}
model.addAttribute("friends", friends);
return "facebook";
}
I took the above request for friends from the project. it shouldn't be hard to tailor the above code I showed with debug_token to use the Spring OAuth rest template. Hope this helps :)

How to generate token in implementation of IUserTokenProvider in Identity 3?

I'm trying to implement IUserTokenProvider interface to register in Identity system and use further for authentication.
Why token authentication because i write Web Api server in Asp Net Core using Identity 3.
The obstacle is i don't know how to generate token. IUserTokenProvider has 2 method i need: 1. generates token 2. validates token.
what do i write inside them? what's algorithm?
public class UserTokenProvider : IUserTokenProvider<AppUser>
{
public Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<AppUser> manager, AppUser user)
{
throw new NotImplementedException();
}
public Task<string> GenerateAsync(string purpose, UserManager<AppUser> manager, AppUser user)
{
/* here logic to generate token */
string result = "generated token";
return Task.FromResult(result);
}
public Task<bool> ValidateAsync(string purpose, string token, UserManager<AppUser> manager, AppUser user)
{
/* validating token */
if (token == "generated token")
return Task.FromResult(true);
else return Task.FromResult(false);
}
}
and in ConfigureServices method of StartUp class i register my provider:
services.AddIdentity<AppUser, IdentityRole>(options => {
options.User.RequireUniqueEmail = true;
})
.AddEntityFrameworkStores<UserDbContext>()
.AddTokenProvider<UserTokenProvider>("AuthToken")
.AddDefaultTokenProviders();
As explained in this other SO post, IUserTokenProvider has absolutely nothing to do with token authentication.
I'd recommend reading these other questions for more information about how you can issue your own access tokens in ASP.NET Core:
Simple JWT authentication in ASP.NET Core 1.0 Web API.
Web API Authentication in ASP.NET 5.
Configure the authorization server endpoint.

WebApi with OWIN SelfHost and Windows Authentication

I have a console application SERVER that hosts WebApi controllers using OWIN self-hosting, and runs under a custom account named "ServiceTest1".
In the same machine I have another console application CLIENT that runs under the account "ServiceTest2", and I want to capture in SERVER that "ServiceTest2" invoked a controller action. However:
WindowsIdentity.GetCurrent() is always "ServiceTest1".
Thread.CurrentPrincipal is an unauthenticated GenericIdentity.
RequestContext.Principal is null.
User is null.
What do I need to make this WebApi OWIN self-hosted to grab the Windows identity of the caller?
Your question is a little unclear on exactly how you've implemented the Windows authentication.
Enable Windows authentication:
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpListener listener = (HttpListener)app.Properties["System.Net.HttpListener"];
listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;
// ...
}
}
Get the user in an OWIN middleware:
public async Task Invoke(IDictionary<string, object> env)
{
OwinContext context = new OwinContext(env);
WindowsPrincipal user = context.Request.User as WindowsPrincipal;
//...
}
Get the user in a Web API Controller:
// In a web api controller function
WindowsPrincipal user = RequestContext.Principal as WindowsPrincipal;

Resources