Validate Access token in service to service authentication using azure oauth2 - azure

I want to enable service to service authentication in my services using spring boot and azure oauth2.I have generated access token using
https://login.microsoftonline.com//oauth2/token (POST)
I want to validate that whenever my service is hit with this access token , it should the request or throw 403.It is always throwing 403.
Hitting after putting header : Authorization -> Bearer access_token
#EnableWebSecurity
#Configuration
public class WebAuthentication extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/subordinate").permitAll();
http.authorizeRequests().antMatchers("/api/**").authenticated();
}
}
What should be added to validate service based authentication token.(No user is involved in generating token)

Related

Microsoft Identity platform (AAD v2.0) protected Azure AD B2C API - expired access token works until restart

I have an API protected with Microsoft Identity platform (AAD v2.0), but although I have configured the access token to have a 5 minute lifetime in the B2C login user flow I can successfully use the access token after it has expired.
I have it setup like this:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(options =>
{
Configuration.Bind("AzureAdB2C", options);
options.TokenValidationParameters.NameClaimType = "name";
},
options => { Configuration.Bind("AzureAdB2C", options); });
services.AddControllers();
services.AddAuthorization(options =>
{
// Create policy to check for the scope 'user_access'
options.AddPolicy("UserAccess",
policy => policy.Requirements.Add(new ScopesRequirement("user_access")));
});
The controller is protected with the attribute: [Authorize]
I call the controller GET method using Postman and the header Authorization: Bearer <access token>
I get the access token using (parameters omitted):
GET https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{policy}/oauth2/v2.0/authorize?
And I get the access token using:
POST https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{policy}/oauth2/v2.0/token
The expires_in field is correctly showing 5 minutes. The login policy is the same as used for the API setup.
If I restart the API before the access token has expired I can continue using it. If I restart the API after it has expired it stops working after restart. If I don't restart it just keeps working.
This is in debug and localhost.
I would have expected the access token to be rejected by the API once it has expired. What have I missed?

Getting error when trying to secure aspnet core web api with B2C authentication: Unable to retrieve openid config

I'm trying to secure my aspnet core web API server by making it authenticate against Azure B2C using user-provided JWT bearer tokens. I've followed some sample code found on official microsoft github pages, but can't seem to get it working.
In my B2C policy, I've got it set to use the default issuer URL format: https:////v2.0/
In my web application, I've got that same URL specified as the Authority in the JWT options.
When I submit an HTTP request to my server, the identity server code fails as it tries to reach out to B2C to fetch the openid-configuration. It fails with the following error ...
HttpRequestException: Response status code does not indicate success: 404 (Not Found).
System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
IOException: IDX20804: Unable to retrieve document from: 'https://innovativelitfoundry.b2clogin.com/0f55bfb6-6af5-4293-8963-29ae099183cc/v2.0/.well-known/openid-configuration'.
Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(string address, CancellationToken cancel)
InvalidOperationException: IDX20803: Unable to obtain configuration from: 'https://innovativelitfoundry.b2clogin.com/0f55bfb6-6af5-4293-8963-29ae099183cc/v2.0/.well-known/openid-configuration'.
Microsoft.IdentityModel.Protocols.ConfigurationManager<T>.GetConfigurationAsync(CancellationToken cancel)
Indeed, that URL will not work because it does not appear to be including the policy name, from the used token, in the query string. So, that URL does indeed not work.
I'm unsure how to make the code provide that policy name in the query string, though? Or should it be doing that automatically?
Here is the code, in my aspnet core web api application, where I configure the authentication settings ...
public void ConfigureServices(IServiceCollection services)
{
IdentityModelEventSource.ShowPII = true;
services
.AddAuthentication(ConfigureAuthentication)
.AddJwtBearer(ConfigureJwt);
services
.AddCors();
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services
.AddSingleton(Configuration);
}
private void ConfigureAuthentication(AuthenticationOptions options)
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}
private void ConfigureJwt(JwtBearerOptions options)
{
var tenant = Configuration["AzureAd:TenantId"];
options.Audience = Configuration["AzureAd:ApplicationId"];
options.Authority = $"https://innovativelitfoundry.b2clogin.com/{tenant}/v2.0/";
}
Does anybody perhaps know what I may be doing incorrectly here? How can I get my aspnet core web api application to correctly pull down that openid configuration document?
You must set options.Authority to an authority URL that includes the policy ID:
options.Authority = $"https://innovativelitfoundry.b2clogin.com/{tenant}/{policy}/v2.0/";
As long as you have set the issuer claim for all policies to the issuer URL that doesn't contain the policy ID, then your API application can download the configuration document for any policy and then validate tokens that are issued for all policies.

How does [Authorize] attribute enhance Azure App Service (web app) authentication/authorization

I published a web app to Azures App Services. I used the App Service's Authentication/Authorization feature to provide security. I successfully added Active Directory features to my web service (and desktop client). It seemed to work very well. Couldn't access data from a browser or desktop client without signing in to the AD.
This was all before I added the [Authorize] attribute to any of the controllers in the API!
So, what will [Authorize] do (or add) to security in my web api. It seems to already be locked up by configuring the Authentication/Authorization features of the web app in Azure.
So, what will [Authorize] do (or add) to security in my web api.
Using ILSpy, you could check the source code about AuthorizeAttribute under System.Web.Mvc.dll. The core code for authorization check looks like this:
protected virtual bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
IPrincipal user = httpContext.User;
if (!user.Identity.IsAuthenticated)
{
return false;
}
if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase))
{
return false;
}
if (_rolesSplit.Length > 0)
{
string[] rolesSplit = _rolesSplit;
IPrincipal principal = user;
if (!rolesSplit.Any(principal.IsInRole))
{
return false;
}
}
return true;
}
The main process would check httpContext.User.Identity.IsAuthenticated, then check whether the current user name, user role is authorized or not when you specifying the allowed Users,Roles.
For Authentication and authorization in Azure App Service(Easy Auth) which is implemented as a native IIS module. Details you could follow Architecture of Azure App Service Authentication / Authorization.
It seemed to work very well. Couldn't access data from a browser or desktop client without signing in to the AD.
This was all before I added the [Authorize] attribute to any of the controllers in the API!
Based on your description, I assumed that you set Action to take when request is not authenticated to Log in with Azure Active Directory instead of Allow Anonymous requests (no action) under your Azure Web App Authentication/Authorization blade.
Per my understanding, you could just leverage App Service Authentication / Authorization which provides built-in authentication and authorization support for you without manually adding middleware in your code for authentication. App service authentication would validate the request before your code can process it. So, for additional custom authorization check in your code, you could define your custom authorize class which inherits from AuthorizeAttribute to implement your custom processing.
public class CustomAuthorize : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//TODO:
}
protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
{
//TODO:
}
}
Then, decorate the specific action(s) or controller(s) as follows:
[CustomAuthorize]
public class UsersController : Controller
{
//TODO:
}
App Service's Authentication/Authorization feature is Based on IIS Level. [Authorize] attribute is based on our code level. Both of this can do Authentication, if you used both of them, it means that there are two levels of authentication in your web app.
Here is a picture that helps you understand them:

How to share an Access Token between an MVC 5 web application and Web API 2 application

In this instance I am having the user log into the (MVC 5) Web application, which then acts as proxy to log into the (Web API 2) API (using basic over SSL) and return a Bearer/Access Token. I'm using the Thinktecture.IdentityModel.Client.OAuth2Client to handle the login and get the access token, which all works fine.
Some other stuff happens but now I want the Web application to be able to decode the Access Token to access the claims set on the API (specifically the user ID returned after login to the API).
I'm using the much demoed, UseOAuthAuthorizationServer and UseOAuthBearerAuthentication extension methods with a token endpoint pretty much out of the box but with a custom OAuthAuthorizationServerOptions.Provider to access my own repository.
I have the same MachineKey on both applications, but I am unclear how to decode the token , although I understand I would probably have to use the SecureDataFormat.Unprotect method.
The closest attempt I have in the Web application is:
Task<TokenResponse> response = client.RequestResourceOwnerPasswordAsync(model.Email, model.Password);
IDataProtector dataProtecter = Startup.DataProtectionProvider.Create("does this matter?");
TicketDataFormat ticketDataFormat = new TicketDataFormat(dataProtecter);
AuthenticationTicket ticket = ticketDataFormat.Unprotect(response.Result.AccessToken);
With the Startup.DataProtectionProvider set as follows:
public partial class Startup
{
internal static IDataProtectionProvider DataProtectionProvider { get; private set; }
public void Configuration(IAppBuilder app)
{
DataProtectionProvider = app.GetDataProtectionProvider();
this.ConfigureAuth(app);
}
}
My fall back plan is to offer an API method that returns the information I am interested in after login, but it seems excessive seeing as it forms part of the claims in the token (as I understand it).
I have tried to wrap my head around JWT (I've looked at Thinktecture, Microsoft source code and various other forums), but not sure if that would help (although claims being available in plain text us useful). I have yet to find an example that allows sign in with basic authentication and returns a custom JWT containing an access token.
Anyway I hope thats enoguh information and any help would be much appreciated... cheers
You guys got very close. Shadi was on the right track. Even if this is a separate mvc application, you still need to decrypt the token as shown and extract the claim. If your web api token is in the variable called accessToken, you can do the following in your mvc web app. (Note that mvc is using both bearer authentication and cookie authentication and OAuthBearerOptions is a static class)
//Unprotect the token
var unencryptedToken = Startup.OAuthBearerOptions.AccessTokenFormat.Unprotect(accessToken);
//Extract the claims identity from the token
var identity = unencryptedToken.Identity;
//Once you have the claims identity extracted, create a new claims identity that uses
//ApplicationCookie as the default authentication type
var id = new ClaimsIdentity(identity.Claims, DefaultAuthenticationTypes.ApplicationCookie);
// Now you are ready to sign in with your new claims identity. You basically created an
//application cookie from a bearer token and now using this cookie to sign-in
AuthenticationManager.SignIn(id);
Here is how your startup.auth.cs should include (note that we have an OAuthBearerAuthenticationOptions static member and we call app.UseOAuthBearerAuthentication() just to be able to decrypt the bearer token
public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
static Startup()
{
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
}
public void ConfigureAuth(IAppBuilder app)
{
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
}
If you are using the UseOAuthBearerAuthentication you can make the OAuthBearerOptions as static on the Startup.Auth.cs class:
public partial class Startup
{
public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
...
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
...
}
}
Now from anywhere in the code you can unprotect the received token this way:
var ticket = Startup.OAuthBearerOptions.AccessTokenFormat.Unprotect(response.Result.AccessToken);
Now you can use the ticket for your access the claims for the API user:
ticket.Identity.Claims
Hope this would answer your question.
EDIT
This answer on SO would solve your problem please have a look.

How do I get this MVC5 client to Login via a remote WebApi2 app?

I'm creating an application in 2 parts. On one server is a .net Webapi2 using Owin. On another server is an MVC5 website with currently no login that will act as a front end for the api. It would also be a nice selling point to show that the app itself is an example of what a client can develop since it relies on the same api. I put the user authentication stuff in the api because I need 3rd parties to be able to develop their own front end apps using the api.
What I'm trying to accomplish (in theory)
I need to have a user submit their login information on the front end it will authenticate them via the resourceownergrant type and recieve results that allow the front end to create a cookie that includes the accesstoken and the identityuser / roles. As long as this cookie exists the mvc app would make calls to the api using the accesstoken. The MVC app and API would both be able to use the [Authorize] attribute.
What I have so far
I have the api up and working, I can post "grant_type=password&username=testuser&password=password123" and I receive something like this in json
{
"access_token":"-longasstokenhere-",
"token_type":"bearer",
"expires_in":1209599,
"userName":"testuser",
".issued":"Thu, 03 Apr 2014 16:21:06 GMT",
".expires":"Thu, 17 Apr 2014 16:21:06 GMT"
}
the web api's response also has a header of
set-cookie: -Long-assserializedcookiestuffhere-
My question is how to connect my mvc app.
in my mvc5 app I've got a startup for owin with this set in the configureauth
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
CookieName = "MyCookie",
LoginPath = new PathString("/Account/Login")
});
I have the [Authorize] attribute set on a test page that when I visit, it correctly redirects me to the login page. The piece I'm missing is how to make it so that clicking login makes the website post back to the api and create an Owin Cookie that the website will use to allow a user past the [authorize] attibute. I would also like it to contain the identityuser as well so the web app will automatically have the user info without having to post back to the api for it on every call. I dont know if I can grab the cookie from the return result somehow or what.
Also if theirs a better way I'm open to suggestions.
Heeelp!?
I had the exact same requirement. I tried to get the MVC5 CookieAuthentication to work, but it wouldn't let me use my own cookie value. But, your WebAPI should not return a set-cookie. WebAPI should be RESTful, and require the client to pass a bearer token on every request.
So, here's my solution. The username and password are sent to an external API, which return a JSON Web Token. The token from the API is stored in a cookie. You could do that in JavaScript, or in an MVC Account controller. That cookie is checked by the MVC app, and if it exists, the cookie indicates proof that the user is logged in to the MVC app, too.
In JavaScript, you pull that token from the cookie, and add it to all requests to the API in the Authorization header as Bearer token. Also, the MVC app can use the value of the cookie (the token) to access all of the user's claims. To logout, just expire the cookie.
First, wire up the app to use Bearer token, with our custom provider
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AllowedAudiences = audienceId.ToArray(),
IssuerSecurityTokenProviders = providers.ToArray(),
Provider = new CookieOAuthBearerProvider("MyCookieName")
{
LoginPath = new PathString("/Account/Login")
}
}
);
Now, I had to use a custom provider, because the Bearer token is stored in the cookie, not in the standard header. I also need to redirect to a login page, rather that simply issuing a 401.
public class CookieOAuthBearerProvider : IOAuthBearerAuthenticationProvider
{
public PathString LoginPath {get; set;}
public string CookieName { get; set; }
public CookieOAuthBearerProvider(string cookieName)
{
if(string.IsNullOrWhiteSpace(cookieName)) {
throw new ArgumentNullException("cookieName");
}
else {
this.CookieName = cookieName;
};
}
public Task ApplyChallenge(OAuthChallengeContext context)
{
if (this.LoginPath.HasValue)
{
context.Response.Redirect(this.LoginPath.Value);
}
return Task.FromResult<object>(null);
}
public Task RequestToken(OAuthRequestTokenContext context)
{
string token = context.Request.Cookies[this.CookieName];
if (!string.IsNullOrEmpty(token))
{
context.Token = token;
}
return Task.FromResult<object>(null);
}
public Task ValidateIdentity(OAuthValidateIdentityContext context)
{
// prove that the cookie token matches this site using context.Ticket.Identity
return Task.FromResult<object>(null);
}
}
Then, anywhere else in your MVC app, you can get the Claims by simply saying:
ClaimsPrincipal.Current

Resources