I am trying to authenticate the Azure Functions v2. I am getting below error
Microsoft.AspNetCore.Authentication.Core: No authentication handler is registered for the scheme 'WebJobsAuthLevel'. The registered schemes are: Bearer. Did you forget to call AddAuthentication().Add[SomeAuthHandler]("WebJobsAuthLevel",...)?.
below is the code I am using in Startup.cs
public class Startup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
builder.Services.AddAuthentication()
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme,o =>
{
o.Audience = "https://*******************.azurewebsites.net/";
o.Authority = "http://localhost:****";
o.RequireHttpsMetadata = false;
o.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
RequireSignedTokens = true,
ValidAudience = "https://***************.azurewebsites.net/",
ValidateAudience = true,
ValidIssuer = "https://sts.windows.net/***************-5********2**/",
ValidateIssuer = true,
ValidateIssuerSigningKey = true,
ValidateLifetime = true
};
});
}
}
changed the code but still getting same given error. Which one I am missing?
Consider using the Azure App Service Authentication/Authorization feature (also known unofficially as EasyAuth). If you follow the express flow in the Azure Portal, it will create an AAD V1 application registration for your function application, and automatically configure your app to allow authentication using AAD. If you then set Action to take when request is not authenticated to Login with Azure Active Directory, only authenticated requests will be authorized to make any request to your application.
EasyAuth has built in support for accepting Bearer tokens for your AAD app registration, as well as some other OAuth2/OIDC flows for AAD and our other supported identity providers (Facebook, Google, Twitter). Through this feature, you shouldn't need to add any code on your end, it will all be handled by the Azure platform.
Related
In my web project i want to enable the user to login with username / password and Microsoft Account.
Tech - Stack:
Asp.Net Core WebApi
Angular
Azure App Service
First i created the username / password login. Like this:
StartUp.cs:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(Configuration["JWTKey"].ToString())),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true
};
});
Login Method:
public async Task<IActionResult> ClassicAuth(AuthRequest authRequest)
{
tbl_Person person = await _standardRepository.Login(authRequest.Username, authRequest.Password);
if (person != null)
{
var claims = new[]
{
new Claim(ClaimTypes.GivenName, person.PER_T_Firstname),
};
var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(_config["JWTKey"].ToString()));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.Now.AddHours(24),
SigningCredentials = creds
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
return Ok(tokenHandler.WriteToken(token));
}
else
return Unauthorized("Invalid login data");
}
And secured my api enpoints with [Authorize].So far so good...that works.
Now i want to add a login method with Microsoft Account. I use Azure App Service Authentication / Authorization for that (https://learn.microsoft.com/de-de/azure/app-service/overview-authentication-authorization).
I configured the auth provider and i'm able to start the auth flow with a custom link in my angular app:
Login with Microsoft - Account
This works and i can retrieve the access token from my angular app with this:
this.httpClient.get("https://mysite.azurewebsites.net/.auth/me").subscribe(res => {
console.log(res[0].access_token);
});
Now the problem:
access_token seems not a valid JWT Token. If i copy the token and go to https://jwt.io/ it is invalid.
When i pass the token to my API i get a 401 - Response. With seems logical because my API checks if the JWT Token is signed with my custom JWT Key and not the Key from Microsoft.
How can I make both login methods work together? I may have some basic understanding problems at the moment.
It seems you want your Angular app calling an ASP.NET Core Web API secured with Azure Active Directory, here is a sample works well for that.
The most important step is register the app in AAD.
By the way, if you want to enable users to login one project with multiple ways in azure, you can use multiple sign-in providers.
We have two Application registered in the same AAD B2C tenant via "New" and "Old" portal.
Authentication with "Old" Application credentials works correct.
With "New" Application credentials - error appears:
IDX10500: Signature validation failed. Unable to resolve SecurityKeyIdentifier: 'SecurityKeyIdentifier
(
IsReadOnly = False,
Count = 1,
Clause[0] = System.IdentityModel.Tokens.NamedKeySecurityKeyIdentifierClause
)
'
Is it right way to use Microsoft.Owin.Security.ActiveDirector library (to protect ASP.Net Web API) with Application registered in AAD B2C tenant.
P.S. My question is based on this post.
You should only create applications via the Azure AD B2C blade in the new Azure portal (portal.azure.com).
Do NOT create applications for Azure AD B2C using the classic Azure Portal (manage.windowsazure.com).
If you want to secure a WebApp, you should use Owin's OpenIdConnectAuthentication. This document has more details on how to do this: Sign-Up & Sign-In in a ASP.NET Web App
If you want to secure a WebAPI, you should use Owin's OAuthBearerAuthentication. This document has more details on how to do this: Build a .NET web API
Samples configuration of a WebApp:
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Generate the metadata address using the tenant and policy information
MetadataAddress = String.Format(AadInstance, Tenant, DefaultPolicy),
// These are standard OpenID Connect parameters, with values pulled from web.config
ClientId = ClientId,
RedirectUri = RedirectUri,
PostLogoutRedirectUri = RedirectUri,
// Specify the callbacks for each type of notifications
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = OnRedirectToIdentityProvider,
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed,
},
// Specify the claims to validate
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name"
},
// Specify the scope by appending all of the scopes requested into one string (separated by a blank space)
Scope = $"{OpenIdConnectScopes.OpenId} {YourScope1} {YourScope2}"
}
);
}
Samples configuration of a Web API:
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)))
});
}
Currently, I am integrating Azure Active Directory into my .NET Web API using following code:
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = WebConfigurationManager.AppSettings["AzureClientId"],
Tenant = WebConfigurationManager.AppSettings["AzureTenant"]
}
});
The audience and tenant were set in web.config file.
I can get the token correctly and users can log in using their azure AD account.
However, I moved the audience and tenant to database to allow users to change the settings and disable/enable azure login by UI instead of changing the setting in web.config.
The above code was changed to:
var azureSetting = db.GetAzureSetting();
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = azureSetting.AzureClientId,
Tenant = azureSetting.AzureTenant
}
});
When the app first starts, there is no config in the database because users do not enter the config yet. Then users go to azure config screen, enter the correct Client Id, Tenant, Client Secret. But users can not log in using azure AD user.
Can anyone explain this case for me?
Is there any way to save azure config in db instead of web.config?
Your code in the post works when web API start and we can’t change configuration runtime , if you want to enable users change the audience and tenant dynamically, you could handle token validation yourself . You could use JwtSecurityTokenHandler to validate the token after your api app get the access token , code below is for your reference :
public JwtSecurityToken Validate(string token)
{
string stsDiscoveryEndpoint = "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration";
ConfigurationManager<OpenIdConnectConfiguration> configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint);
OpenIdConnectConfiguration config = configManager.GetConfigurationAsync().Result;
TokenValidationParameters validationParameters = new TokenValidationParameters
{
ValidateAudience = true,
ValidateIssuer = false,
ValidAudience = "https://testbasic1.onmicrosoft.com/TodoListService", //your value from database
IssuerSigningTokens = config.SigningTokens,
ValidateLifetime = false
};
JwtSecurityTokenHandler tokendHandler = new JwtSecurityTokenHandler();
SecurityToken jwt;
var result = tokendHandler.ValidateToken(token, validationParameters, out jwt);
return jwt as JwtSecurityToken;
}
This handler helps you verify the signature of the token to ensure the token was issued by Azure Active Directory,and also verify the claims in the token based on the business logic,in your scenario,you need to confirm audience and tenant.
In you web api app , you could register custom TokenValidationHandler in Global.asax :
GlobalConfiguration.Configuration.MessageHandlers.Add(new TokenValidationHandler());
You could click here for code sample , you could modify your code to check whether tokens comes from tenant-id which stores in database .
The examples I've seen for using Azure B2C with Web API show app.UseOAuthBearerAuthentication (as shown below), however my ASP .NET 5 Web API project uses IApplicationBuilder (not IAppBuilder) and UseOAuthBearerAuthentication does not exist. I've tried app.UseOpenIdConnectAuthentication, however I believe this uses cookies and I couldn't get it working using a Xamarin app as a client. I've tried app.UseWindowsAzureActiveDirectoryBearerAuthentication but I believe this is for standard Azure AD (not B2C) is that true? Any ideas how to get Azure B2C working with the very latest ASP .NET Web API?
Thanks!!!
public void ConfigureAuth(IAppBuilder app)
{
TokenValidationParameters tvps = new TokenValidationParameters
{
// This is where you specify that your API only accepts tokens from its own clients
ValidAudience = clientId,
};
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, "v2.0", discoverySuffix, commonPolicy)))
});
}
This works for me. I hope it helps someone else who is looking to use Azure B2C with the latest .NET Web API framework:
public void ConfigureAuth(IApplicationBuilder app, IOptions<PolicySettings> policySettings)
{
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AuthenticationScheme = JwtBearerDefaults.AuthenticationScheme,
AutomaticAuthenticate = true,
AutomaticChallenge = true,
MetadataAddress = "https://login.microsoftonline.com/[my-tenant].onmicrosoft.com/v2.0/.well-known/openid-configuration?p=B2C_1_my-signup-signin-policy",
Audience = "[My-Azure-App-Guid]",
Events = new JwtBearerEvents
{
OnTokenValidated= ctx =>
{
var nameClaim = ctx.AuthenticationTicket.Principal.FindFirst("name");
if (nameClaim != null)
{
var claimsIdentity = (System.Security.Claims.ClaimsIdentity)ctx.AuthenticationTicket.Principal.Identity;
claimsIdentity.AddClaim(new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Name, nameClaim.Value));
}
return Task.FromResult(0);
},
OnAuthenticationFailed = ctx =>
{
ctx.SkipToNextMiddleware();
return Task.FromResult(0);
}
}
});
}
If the JWT token is available in your client, then you can use JwtBearerAuthentication in the Web API. The JWT issued by Azure B2C ( social logins) can be used as a token to get authenticated in the Web API.
Refer to https://github.com/sendhilkumarg/AzureB2CWebAppAndAPIAuthentication
In this sample the client is a web app
We are successfully authenticating the Azure AD users from different subscription using Azure AD Multi-tenant application but unable to authenticate the Windows Live ID accounts.
To authenticate the live ID accounts we use the Windows Live ID identity provider with Azure Access Control Service (ACS), its working fine with Azure AD single tenant application but we are struggling to authenticate Azure AD users across subscriptions which can only be done by using the Azure AD multi-tenant application.
We follow this blog https://msdn.microsoft.com/en-us/library/azure/dn486924.aspx and it works for Single tenant application but when we try to configure the Azure AD app to multi-tenant and configure it with ACS getting the below error.
enter image description here
Is there any approach we authenticate the Windows Live ID and use the Azure Multi-Tenant Application?
You can authenticate Microsoft Account (live id) users in a multi tenant application by skipping ACS altogether and provisioning the Microsoft Account in directory tenants. One gotcha is that authenticating with a Microsoft Account requires you to fully specify the authentication endpoints by instantiating the tenant in the URL. You cannot use the /common endpoint because that relies on the user's home tenant, and an MSA user does not have one.
You add following code in your Account controller
public void SignIn(string directoryName = "common")
{
// Send an OpenID Connect sign-in request.
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Environment.Add("Authority", string.Format(ConfigurationManager.AppSettings["ida:Authority"] + "OAuth2/Authorize", directoryName));
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
and add this block in your startup.auth.cs
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = ClientId,
Authority = Authority,
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
// we inject our own multitenant validation logic
ValidateIssuer = false,
},
Notifications = new OpenIdConnectAuthenticationNotifications()
{
RedirectToIdentityProvider = (context) =>
{
object obj = null;
if (context.OwinContext.Environment.TryGetValue("Authority", out obj))
{
string authority = obj as string;
if (authority != null)
{
context.ProtocolMessage.IssuerAddress = authority;
}
}
if (context.OwinContext.Environment.TryGetValue("DomainHint", out obj))
{
string domainHint = obj as string;
if (domainHint != null)
{
context.ProtocolMessage.SetParameter("domain_hint", domainHint);
}
}
context.ProtocolMessage.RedirectUri = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path);
context.ProtocolMessage.PostLogoutRedirectUri = new UrlHelper(HttpContext.Current.Request.RequestContext).Action
("Index", "Home", null, HttpContext.Current.Request.Url.Scheme);
//context.ProtocolMessage.Resource = GraphAPIIdentifier;
context.ProtocolMessage.Resource = AzureResourceManagerIdentifier;
return Task.FromResult(0);
},
...
}
When you click on "SignIn" ask for "Azure AD name". Pass that variable to the Account/SignIn action. If the user will be present in the mentioned Azure AD, sign-in will be successful.