How to Authorize a Scheduled Task with a B2C protected Web API? - authorization

I have a B2C protected Web API hosted on Azure.
I need to create a Scheduled Task (or web Service) which will need to consume resources from the Web Api.
What is the recommended approach to get the Scheduled Task to authenticate with the Web Api?
I don't think that I can use B2C in this scenario. I was thinking of using secret keys but I can't quite figure out how to set this up in Azure as well as how to update the Web Api OWIN middleware to handle both B2C and key authentication.
Here is my current Startup.Auth:
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)))
});
}
Cheers!

You can use Resource Owner Password Credenetial(ROPC) user flow
https://learn.microsoft.com/en-us/azure/active-directory-b2c/configure-ropc

Related

How to Access MVC API which is authorize with IdentityServer4

On Owin middleware Startup class I have added OIDC Authentication where response type is "code id_token". With this middleware I can access my authorized controller. But the problem is, I can't access my API in the same domain with this middleware.
I am using the access_token that i stored in the userClaim. But it is returning the HTML of IdentityServer4 login page.
[Filters.AuthorizeOIDC(Roles = "dukkan.sa")]
public async Task<ActionResult> ViewApiResult()
{
var user = User as System.Security.Claims.ClaimsPrincipal;
var token = user.FindFirst("access_token").Value;
var result = await CallApi(token);
ViewBag.Json = result;
return View();
}
private async Task<string> CallApi(string token)
{
var client = new HttpClient();
client.SetBearerToken(token);
var json = await client.GetStringAsync("http://localhost:57346/api/SampleApi");
return json;
}
The examples I got to secure MVC API is with IdentityServer3. They are using IdentityServer3.AccessTokenValidation package to authenticate the client from back channel during the API Access request:
app.UseOAuthBearerAuthentication(new IdentityServerBearerTokenAuthenticationOptions { Authority = "https://localhost:44319/identity", RequiredScopes = new[] { "sampleApi" } });
But IdentityServer4.AccessTokenValidation is not working with MVC5. I can use IdentityServer3.AccessTokenValidation in MVC 5. But this is accepting IdentityModel with version bellow 2.0.0.
Need solution for it. IdentityServer4 is not supporting properly for MVC.
Why do you want to use IdentityServer4.AccessTokenValidation with MVC5? Because the server is IdentityServer4?
There is no need for that. IdentityServer3 and IdentityServer4 are build on the same OpenId Connect specifications, meaning that you can use IdentityServer3.AccessTokenValidation for the client while the server is IdentityServer4.
In fact you can use any piece of code on the client that is build according to the specifications of OpenId Connect. I suggest you give IdentityServer3.AccessTokenValidation a try.

Trouble getting ClaimsPrincipal populated when using EasyAuth to authenticate against AAD on Azure App Service in a Asp.Net Core web app

We have a web app built on Asp.Net core. It doesn't contain any authentication middleware configured in it.
We are hosting on Azure App Service and using the Authentication/Authorization option (EasyAuth) to authenticate against Azure AD.
The authentication works well - we get the requisite headers inserted and we can see the authenticated identity at /.auth/me. But the HttpContext.User property doesn't get populated.
Is this a compatibility issue for Asp.Net core? Or am I doing something wrong?
I've created a custom middleware that populates the User property until this gets solved by the Azure Team.
It reads the headers from the App Service Authentication and create a a user that will be recognized by the [Authorize] and has a claim on name.
// Azure app service will send the x-ms-client-principal-id when authenticated
app.Use(async (context, next) =>
{
// Create a user on current thread from provided header
if (context.Request.Headers.ContainsKey("X-MS-CLIENT-PRINCIPAL-ID"))
{
// Read headers from Azure
var azureAppServicePrincipalIdHeader = context.Request.Headers["X-MS-CLIENT-PRINCIPAL-ID"][0];
var azureAppServicePrincipalNameHeader = context.Request.Headers["X-MS-CLIENT-PRINCIPAL-NAME"][0];
// Create claims id
var claims = new Claim[] {
new System.Security.Claims.Claim("http://schemas.microsoft.com/identity/claims/objectidentifier", azureAppServicePrincipalIdHeader),
new System.Security.Claims.Claim("name", azureAppServicePrincipalNameHeader)
};
// Set user in current context as claims principal
var identity = new GenericIdentity(azureAppServicePrincipalIdHeader);
identity.AddClaims(claims);
// Set current thread user to identity
context.User = new GenericPrincipal(identity, null);
};
await next.Invoke();
});
Yes, this is a compatibility issue. ASP.NET Core does not support flowing identity info from an IIS module (like Easy Auth) to the app code, unfortunately. This means HttpContext.User and similar code won't work like it does with regular ASP.NET.
The workaround for now is to invoke your web app's /.auth/me endpoint from your server code to get the user claims. You can then cache this data as appropriate using the x-ms-client-principal-id request header value as the cache key. The /.auth/me call will need to be properly authenticated in the same way that calls to your web app need to be authenticated (auth cookie or request header token).
I wrote a small basic middleware to do this. It will create an identity based off of the .auth/me endpoint. The identity is created in the authentication pipeline so that [authorize] attributes and policies work with the identity.
You can find it here:
https://github.com/lpunderscore/azureappservice-authentication-middleware
or on nuget:
https://www.nuget.org/packages/AzureAppserviceAuthenticationMiddleware/
Once added, just add this line to your startup:
app.UseAzureAppServiceAuthentication();
The following code decrypts the AAD token from the Azure App Service HTTP header and populates HttpContext.User with the claims. It's rough as you'd want to cache the configuration rather than look it up on every request:
OpenIdConnectConfigurationRetriever r = new OpenIdConnectConfigurationRetriever();
ConfigurationManager<OpenIdConnectConfiguration> configManager = new ConfigurationManager<OpenIdConnectConfiguration>(options.Endpoint, r);
OpenIdConnectConfiguration config = await configManager.GetConfigurationAsync();
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKeys = config.SigningKeys.ToList(),
ValidateIssuer = true,
ValidIssuer = config.Issuer,
ValidateAudience = true,
ValidAudience = options.Audience,
ValidateLifetime = true,
ClockSkew = new TimeSpan(0, 0, 10)
};
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
ClaimsPrincipal principal = null;
SecurityToken validToken = null;
string token = context.Request.Headers["X-MS-TOKEN-AAD-ID-TOKEN"];
if (!String.IsNullOrWhiteSpace(token))
{
principal = handler.ValidateToken(token, tokenValidationParameters, out validToken);
var validJwt = validToken as JwtSecurityToken;
if (validJwt == null) { throw new ArgumentException("Invalid JWT"); }
if (principal != null)
{
context.User.AddIdentities(principal.Identities);
}
}
It only works for Azure AD. To support other ID providers (Facebook, Twitter, etc) you'd have to detect the relevant headers and figure out how to parse each provider's token. However, it should just be variations on the above theme.
You can give this library a try. I faced a similar problem and created this to simplify the use.
https://github.com/dasiths/NEasyAuthMiddleware
Azure App Service Authentication (EasyAuth) middleware for ASP.NET
CORE with fully customizable components with support for local
debugging
It hydrates the HttpContext.User by registering a custom authentication handler. To make things easier when running locally, it even has the ability to use a json file to load mocked claims.

Asp.net mvc web use both token base authentication and form authentication

I have an asp.net mvc project that contains some web API controllers.my mvc area and pages are authenticated via form authentication.
API controllers should be consumed from native android client I need to register deice and authenticate them for some API's. I searched and seen some web api example used token authentication but here how can i merged both token and form authentication for different request?
how can i customize my security configuration to generate token and authenticate api requests?
here is Startup.Auth class:
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager> (ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
// Enables the application to remember the second login verification factor such as phone or email.
// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
// This is similar to the RememberMe option when you log in.
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
}

Authenticating and Authorizing using ADFS WS-Fed protocol

I am working on implementing Authenticating and Authorization in my application.
For Authentication:
I configured by ADFS Server with WS-Fed Sign in Protocol and enabled JWT. Created MVC application and configured to use WS-Fed for authenticating user.
Now question here is how do I store JWT token in my cookie after successfully login?
Here is my code
public partial class Startup
{
private static string realm = ConfigurationManager.AppSettings["ida:Wtrealm"];
private static string adfsMetadata = ConfigurationManager.AppSettings["ida:ADFSMetadata"];
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions{ CookieName="JwtToken",CookieHttpOnly=false});
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Wtrealm = realm,
MetadataAddress = adfsMetadata
});
}
}
For Authorization
I have a separate Web API project. I want to authorize my api's by passing the JWT token in header of every request but not sure how to extract JWT token from cookie and pass it to web api for validating.
I found the answer here http://www.software-architects.com/devblog/2015/02/02/ADFS-and-ADAL-Lab.
ADAL (Active Directory Authentication Libraryfor .NET and for JavaScript) which can be used to acquire token in mvc application and pass the token in header for authorizing web api.

OWIN Cookie authentication in Self-Hosted Web Api

I have an OWIN Self-hosted Web Api and some MVC web applications all in the same domain. Web applications are calling the Web Api in server side. They use OWIN cookie authentication like this:
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext<MyUserManager>(MyUserManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity =
SecurityStampValidator.OnValidateIdentity<MyUserManager, MyUser, Guid>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentityCallback: (manager, user) =>
user.GenerateUserIdentityAsync(manager),
getUserIdCallback: (id) => (new Guid(id.GetUserId())))
}
});
}
Being in the same domain, when user sign in one web application, the cookies are available in other web applications and the user is signed in.
I implement cookie authentication in my self-hosted web api like this:
public void Configuration(IAppBuilder appBuilder)
{
// Configure Web API for self-host.
HttpConfiguration config = new HttpConfiguration();
config.MessageHandlers.AddRange(new List<DelegatingHandler>
{
new ServerContextInitializerHandler(), new LogRequestAndResponseHandler(),
});
config.MessageHandlers.AddRange(ServiceLocator.Current.GetAllInstances<DelegatingHandler>());
config.Services.Add(typeof(IExceptionLogger), new GlobalExceptionLogger());
config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
appBuilder.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "ApplicationCookie",
Provider = new CookieAuthenticationProvider(),
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active
});
appBuilder.UseWebApi(config);
}
I expect to have the user in web api, as it's in the same domain and the cookies are all available in received requests.
The problem is the Request.GetRequestContext().Principal is null (and other alternatives like Request.GetOwinContext().Authentication.User).
I emphasis that the Request.GetOwinContext().Request.Cookies contains all cookies that are available in the web application.
It has passed a long time from asking the question, but there is no answer yet, but I found the reason and I'm just sharing it. Hope to be useful.
As I mentioned, I implemented cookie authentication in my self-hosted web api, and the authentication cookie was available in the received request in web api.
The problem is, In web applications (where the users signed in) OWIN middle-ware is encoding the ClaimsIdentity data into an Access Token and put it in the authentication cookie, and to access the authentication data in this cookie, decode it's content. This encoding and decoding on web applications is done by encryption and decryption using Machine Key of the machine on which the server is running, and in self hosted web api using another way named DPAPI.
So, I had the authentication cookie on web api request, but because the OWIN middle-ware tried to do decryption using DPAPI, it wasn't able to extract the Access Token from the cookie.