I'm building a small Azure Serverless Application with 3 distinct functions triggered by 3 different HTTP events. I set the "Authorization Level" to "Anonymous" for each of the functions. Then I set up the Authentication (on the application level) to link to my Azure Active Directory. Although it took me some time to figure out that part, in the end it appears to work like a charm. The issue is that right now I'm required to pass the bearer token for each and every one of these functions, whereas I need one of them to be publicly accessible.
Is there any way to do that that does not require me to split that one function into a separate Azure Application?
I think URL authorization rules should help here.
For your scenario, Enable the Authentication/Authorization and allow anonymous access in the portal. Next, you'll want to create an authorization.json file in the root of your site and define two routes:
Disable anonymous access at the root.
Enable anonymous access for the anonymous function URL.
authorization.json
{
"routes": [{
"path_prefix": "/",
"policies": { "unauthenticated_action": "RedirectToLoginPage" }
},{
"path_prefix": "/api/HttpTrigger1",
"policies": { "unauthenticated_action": "AllowAnonymous" }
}]
}
NOTE: Make sure to Stop/Start Function App after enabling the Authentication/Authorization and adding the authorization.json file at wwwroot folder.
Not sure how you implemented the app level security but in a regular OWIN or CORE app, the token handler will just pass the request as unauthenticated to the controller method if it does not find a valid token in the Authorization header (no header or token invalid). It's up to the method then to decide whether to handle the request or reject as unauthorized. Can you change your token verifier accordingly? (In fact, if your API can receive tokens issued by different issuers - AAD, B2C, Ping - you can configure several token handlers, each will try to va
Related
I have two applications -
public client application (.NET Core console app), in which user gets Microsoft identity access token
web API, which tries to call Microsoft Graph on-behalf-of user, using that access token
When I call Microsoft Graph from web API, I get a MicrosoftIdentityWebChallengeUserException, which inner exception states:
"The user or administrator has not consented to use the application with ID <...> named <...>. Send an interactive authorization request for this user and resource."
I've tried:
to pre-authorize client application in service application using Expose an API tab in Azure Portal
to add client application ID in knownClientApplications array in Manifest tab
to include the scopes, needed for Microsoft Graph (e.g. "User.Read"), in the access token, obtained by the user
but it seems that this does not work and I still get the same exception.
The question is - can I somehow avoid this exceptional situation by getting all needed permissions in a user access token, before calling the GRPC service, or if not, that how do I need to handle this exception to propagate it back to the user.
Full details here. Keep following the Next Steps.
Basically, you'll need to:
Include the Microsoft.Identity.Web and Microsoft.Identity.Web.MicrosoftGraph NuGet packages in your API project.
Set up a Client Secret or a Certificate in the Azure App Registration. Include that in your appsettings.json file:
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "{YOUR-DOMAIN-NAME-FROM-APP-REGISTRATION}",
"TenantId": "{YOUR-TENANT-ID-FROM-APP-REGISTRATION}",
"ClientId": "{YOUR-CLIENT-ID-FROM-APP-REGISTRATION}",
"Scopes": "{YOUR-API-ACCESS-SCOPE-FROM-APP-REGISTRATION}",
"CallbackPath": "/signin-oidc",
"ClientSecret": "{YOUR-CLIENT-SECRET-FROM-APP-REGISTRATION}"
}
Include the following section in your appsettings.json file:
"Graph": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": "User.Read"
}
Include the following code in your Project.cs file or Startup.cs file (depending on what version of .Net you're using):
Startup.cs:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration, Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddMicrosoftGraph(Configuration.GetSection("Graph"))
.AddInMemoryTokenCaches();
Project.cs:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddMicrosoftGraph(builder.Configuration.GetSection("Graph"))
.AddInMemoryTokenCaches();
From there, you just need to inject the GraphServiceClient into your controller or page constructor. The link above provides code for implementation in an ASP.NET API. I'm using this method in a Blazor Webassembly hosted app, so my implementation needs varied slightly from the instructions, but it's running/working as it should.
We have an azure static web app with authentication provided by azure active directory (company tenant)
This works for production but when we open a PR and get a staging url generated it appends an identifier e.g. https://salmon-mountain-xxxxxxx-20.azurestaticapps.net
Obviously this isn't set as a reply/redirect url in the app registration so auth fails.
The site is currently on the public internet but only organisation AD accounts can log in. Is my only option adding a wildcard in the manifest? I know wildcards aren't generally recommended and this functionality may vanish in the future
• You have the option of adding a user to a role, generate invitations to associate specific users to a specific role through the azure portal by adding a comma separated list of the role names to be assigned. These can be defined in staticwebapp.config.json.file and this file can be located in ‘app_location’ in the workflow file of the app build.
• Once the external users for whom the app to be accessed are invited, you now must check the route rules for any change in the authentication process. Routes are secured by adding one or more role names into a rule's allowedRoles array. By default, every user belongs to the built-in anonymous role, and all logged-in users are members of the authenticated role. Optionally, users are associated to custom roles via invitations. For instance, to restrict a route to only authenticated users, add the built-in authenticated role to the allowedRoles array as below: -
{
"route": "/profile",
"allowedRoles": ["authenticated"]
}
• Also, check the response overrides that are configured for your static web app. They should be as follows: -
{
"responseOverrides": {
"400": {
"rewrite": "/invalid-invitation-error.html"
},
"401": {
"statusCode": 302,
"redirect": "/login"
},
"403": {
"rewrite": "/custom-forbidden-page.html"
},
"404": {
"rewrite": "/custom-404.html"
}
}
}
• Also, check the networking section to control the network configuration of your static web app. It has details of all the IP addresses that are allowed to access your app.
Please check the below link for more information on configuring the app files of your static web app for options on configuring other than wildcard details in the app manifest file: -
https://learn.microsoft.com/en-us/azure/static-web-apps/configuration#example-configuration-file
https://learn.microsoft.com/en-us/azure/static-web-apps/authentication-authorization
I am using Azure AD to secure my service to service calls. (Each service having an application identity in Azure AD).
Example: Application A wants to access Application B.
I noticed that when requesting an accesstoken from Application A using Client Credential Flow (with Certificate), an accesstoken is issued without having me to explicitly set the permissions to access Application B.
This seems odd to me because the token returned has its audience set to Application B even thought I haven't explicitly given it access.
If I understand correctly, all registered app have access to each other by default?
Is there a way in Azure AD to explicitly require permissions to be set in order for application to access each other?
Below is a screenshot of Application A required permissions. As you can see, Application B is not listed here.
In the following screenshot, I assigned TodoListService (aka Application B) to the required permissions of Application A
I noticed that when requesting an accesstoken from Application A using Client Credential Flow (with Certificate), an accesstoken is issued without having me to explicitly set the permissions to access Application B.
Yeah, that one can be a bit surprising and I'm not sure why that is the case either.
What you need to do is define application permissions on the API, and then assign it on the client.
Then you need to check the caller has the required app permission in the token.
I have an article on this topic: Defining permission scopes and roles offered by an app in Azure AD.
To define an app permission on the API, you'll have to edit its manifest in Azure AD, and add an app role with member type of Application, something like:
{
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"displayName": "Read all todo items",
"id": "f8d39977-e31e-460b-b92c-9bef51d14f98",
"isEnabled": true,
"description": "Allow the application to read all todo items as itself.",
"value": "Todo.Read.All"
}
]
}
IIRC you have to generate a GUID for the id.
After defining this permission on the API, go to your client app, and add the app permission in the Required permissions.
Then you should press Grant permissions to grant the app permission.
Now then when the client acquires a token with client credentials, the token will contain:
{
"roles": [
"Todo.Read.All"
]
}
So you'll have to check that that is present.
I have an asp.net core web application hosted in azure as an app service. I've configured the application to use OpenId Connect with Azure AD as the authority. The authentication happens within my application (I am not configuring the app service itself to handle the authentication).
Everything works fine when I hit the app service directly (or even if I use a custom domain name).
I've enabled the CDN service within the app service. Once the initial propagation finished, I open a browser and navigate to the CDN address ([name].azureedge.net).
I get a redirect to Azure AD, but once I finish the authentication process, I get an error.
It looks like when the redirect to Azure AD came back from the CDN, the app service's URL was set as the return_url. So when Azure AD redirected me, I was no longer hitting the CDN. When the redirect to Azure AD happens, there's a cookie placed in my browser; I suspect my site is looking for that cookie, but the browser didn't send it because it was set by a different domain.
I've tried configuring the CallbackPath in the OpenIdConnectOptions to the full URL (schema, host, domain, etc), but when my application initializes, and error is thrown saying that the path must start with a '/' (presumably it's expecting a path from the root of the domain in the request).
Hopefully someone else has come across this problem and can tell me what I'm doing wrong.
Per request, here's my OIDC configuration:
var openIdOptions = new OpenIdConnectOptions
{
ClientId = adSettings.ClientId,
ClientSecret = adSettings.ClientSecret,
Authority = adSettings.Authority,
CallbackPath = adSettings.CallbackPath,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
Events = new OpenIdConnectEvents { OnTicketReceived = AddApplicationRolesToUserClaimsAsync, OnAuthorizationCodeReceived = RedeemCodeAsync }
};
foreach (var scope in adSettings.Scopes.Concat(settings.MicrosoftGraph.Scopes))
openIdOptions.Scope.Add(scope);
application.UseOpenIdConnectAuthentication(openIdOptions);
adSettings is a POCO that is hydrated from the following appsettings.json:
"AzureAd": {
"AADInstance": "https://login.microsoftonline.com/",
"ClientSecret": "REDACTED",
"CallbackPath": "/signin-oidc",
"ClientId": "REDACTED",
"TenantId": "REDACTED",
"Scopes": [
"openid",
"profile",
"offline_access"
]
}
adSettings.Authority is defined in the POCO as:
public string Authority => $"{AADInstance}{TenantId}/v2.0";
After digging around a bit, I found the answer.
The OpenIdConnectOptions.Events property allows you to hook into various events that happen throughout the lifecycle of authentication. One callback is called OnRedirectToIdentityProvider. It provides a RedirectContext. On that object you can read/write to a property called ProtocolMessage.RedirectUri. That property allows you to specify a full URL which is used as the return_url when the user is forwarded to AAD.
It's worth noting that I'm using the Microsoft.AspNetCore.Authorization.OpenIdConnect packge from Nuget. There are other packages available that provide similar functionality that do allow you to set a full URL in the options object.
We are trying to set up an authentication/authorization-process with the following requirements:
Authentication: Done by Azure AD.
Authorization: Only members of a specific security groups should be allowed to access the app.
While the authentication part seems to work without problems, we are stuck at the authorization part. We are using Express and Passport.
Azure AD some tokens to req.headers, e.g.
x-ms-token-aad-access-token
x-ms-token-aad-refresh-token
x-ms-token-aad-id-token
We are currently using the id-token together with the passport-azure-ad BearerStrategy to check the security groups of the user against the allowed security groups.
The problem is: As soon as the id-token expires, the application won't let us access the app. Assuming setting {session: true} in passport could solve this issue, we enabled the session, but no luck.
Doing some more research I found this post: How to refresh an ID Token from Azure AD in a Web App?, which states that only access-tokens can be refreshed, but ID tokens cannot and should not.
Examining the x-ms-token-aad-access-token and the x-ms-token-aad-refresh-token, we found that they don't have the JWT-structure, e.g
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1THdqcHdBSk9NOW4tQSJ9.eyJhdWQiOiJodHRwczovL3NlcnZpY2UuY29udG9zby5jb20vIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvN2ZlODE0NDctZGE1Ny00Mzg1LWJlY2ItNmRlNTdmMjE0NzdlLyIsImlhdCI6MTM4ODQ0MDg2MywibmJmIjoxMzg4NDQwODYzLCJleHAiOjEzODg0NDQ3NjMsInZlciI6IjEuMCIsInRpZCI6IjdmZTgxNDQ3LWRhNTctNDM4NS1iZWNiLTZkZTU3ZjIxNDc3ZSIsIm9pZCI6IjY4Mzg5YWUyLTYyZmEtNGIxOC05MWZlLTUzZGQxMDlkNzRmNSIsInVwbiI6ImZyYW5rbUBjb250b3NvLmNvbSIsInVuaXF1ZV9uYW1lIjoiZnJhbmttQGNvbnRvc28uY29tIiwic3ViIjoiZGVOcUlqOUlPRTlQV0pXYkhzZnRYdDJFYWJQVmwwQ2o4UUFtZWZSTFY5OCIsImZhbWlseV9uYW1lIjoiTWlsbGVyIiwiZ2l2ZW5fbmFtZSI6IkZyYW5rIiwiYXBwaWQiOiIyZDRkMTFhMi1mODE0LTQ2YTctODkwYS0yNzRhNzJhNzMwOWUiLCJhcHBpZGFjciI6IjAiLCJzY3AiOiJ1c2VyX2ltcGVyc29uYXRpb24iLCJhY3IiOiIxIn0.JZw8jC0gptZxVC-7l5sFkdnJgP3_tRjeQEPgUn28XctVe3QqmheLZw7QVZDPCyGycDWBaqy7FLpSekET_BftDkewRhyHk9FW_KeEz0ch2c3i08NGNDbr6XYGVayNuSesYk5Aw_p3ICRlUV1bqEwk-Jkzs9EEkQg4hbefqJS6yS1HoV_2EsEhpd_wCQpxK89WPs3hLYZETRJtG5kvCCEOvSHXmDE6eTHGTnEgsIk--UlPe275Dvou4gEAwLofhLDQbMSjnlV5VLsjimNBVcSRFShoxmQwBJR_b2011Y5IuD6St5zPnzruBbZYkGNurQK63TJPWmRd3mbJsGM0mf3CUQ
They don't contain any dots and thus don't pass the JWT-verification.
Resulting in the following question:
What is the correct way to check security groups of a user against specified allowed security groups?
You can do it through passport in one call, you do not need to do extra calls to other api layers, as seams to be suggested in multiple posts online. Using the v2 endpoint and defining a scope you can choose what you have access to and what you receive back in the token. Some options, including security group do require you to modify the manifest, see below.
In your Azure Active Directory go to the App registration you are using the authenticate users. In the manifest for that app registration change the groupMembershipClaims from null to "SecurityGroup" or "All" if want to include office groups etc.
{
"id": "some-id",
"accessTokenAcceptedVersion": null,
"allowPublicClient": false,
"appId": "some-id",
"appRoles": [],
"oauth2AllowUrlPathMatching": false,
"createdDateTime": "2018-11-15T17:49:23Z",
"groupMembershipClaims": "SecurityGroup",
"identifierUris": [ ...............
It then populates the Groups field with an array of the groups using their Object ID.
So you can get the array at req.user._json.groups
if (req.user._json.groups.indexOf('some-group-Object-ID') > -1) {
//In Group
} else {
//Not in Group
}
Apart from checking group claims in ID token , you could also call microsoft graph api to get the groups that the user is a direct member of :
POST https://graph.microsoft.com/beta/me/getMemberGroups
Content-type: application/json
Content-length: 33
{
"securityEnabledOnly": true
}
Or using Auzre AD Graph api :
POST https://graph.windows.net/myorganization/{resource_collection}/{resource_id}/getMemberGroups?api-version
Content-Type: application/json
{
"securityEnabledOnly": false
}
For how an App Service Web, Mobile, or API app can be configured to call the Azure Active Directory Graph API on behalf of the logged-in user. You could refer to below document which show detail steps:
https://cgillum.tech/2016/03/25/app-service-auth-aad-graph-api/
After authentication, access token can be fetched directly from a built-in request header (x-ms-token-aad-access-token) and you could make a http request to call graph api to get the group information .