Need of scope parameter in Microsoft.Identity.Web downstream API - .net-core

I am using microsoft.Identity.Web package on my .netcore API project which calls Graph API to get the directory objects of the user.
In the appsettings file the downstream api settings are provided as below,
"DownstreamApi": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": "Directory.Read.All"
},
The relevant permission(Directory.Read.All) is setup in the app registration.
But even if I leave the "Scope" parameter blank the API is giving me the directory objects.
So if the settings is of the format below it still works. Then what is the need of this scope parameter?
"DownstreamApi": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": ""
},

The scope claim might not had reflected in the token and so you might not seeing any difference with scope assigned.
user_impersonation is the default delegated permission /scope that exists initially for every Web app or API in Azure AD.
Please make sure to add the required delegated permissions or application permission in portal.And grant consent if required.
In your case add directory.read.all Application permission
ex:I added user.read
Appsettings:
"DownstreamApi": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": "user.read"
},
In startUp.cs
Public void ConfigureServices(IServiceCollection services)
{
string[] initialScopes = Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration)
// acquire a token to call a protected web API
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
.AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
.AddInMemoryTokenCaches();
//
//othercode
...
}
And in controller we need to specify scopes and send to request headers to get access token for required scopes.
References:
call Microsoft Graph | Microsoft Docs
(OR) active-directory-aspnetcore-webapp-openidconnect-v2 (github.com)
How can I create a new Azure App Registration without the user_impersonation OAuth2Permission? - Stack Overflow
If client_credentials is the grant type you may need to use https://graph.microsoft.com/.default for scope in the application settings which will give you the permissions defined for your app.
"DownstreamApi": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": "https://graph.microsoft.com/.default"
}
Try to use /token endpoint in request and not common
Please see:
ASP.NET Core - Call Graph API Using Azure Ad Access Token - Stack Overflow-Reference

Related

Firebase 3rd-party AuthProvider (Google/Facebook/etc) login with chrome extension manifest v3

Manifest version 3 for Chrome extensions have been killing me lately. Been able to navigate around it so far, but this one has really stumped me. I'm trying to use Firebase authentication for a Chrome extension, specifically with 3rd party auth providers such as Google and Facebook. I've setup the Firebase configuration for Login with Google and created a login section in the options page of the Chrome extension and setup the Firebase SDK.
Now, there are two login options when using an auth provider, signInWithRedirect and signInWithPopup. I've tried both of these and both have failed for different reasons. signInWithRedirect seems like a complete dead end as it redirects to the auth provider, and when it attempts to redirect back to the chrome-extension://.../options.html page, it just redirects to "about:blank#blocked" instead.
When attempting to use signInWithPopup, I instead get
Refused to load the script 'https://apis.google.com/js/api.js?onload=__iframefcb776751' because it violates the following Content Security Policy directive: "script-src 'self'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
In v2, you could simply add https://apis.google.com to the content_security_policy in the manifest. But in v3, the docs say
"In addition, MV3 disallows certain CSP modifications for extension_pages that were permitted in MV2. The script-src, object-src, and worker-src directives may only have the following values:"
self
none
Any localhost source, (http://localhost, http://127.0.0.1, or any port on those domains)
So is there seriously no way for a Google Chrome extension to authenticate with a Google auth provider through Google's Firebase? The only workaround I can think of is to create some hosted site that does the authentication, have the Chrome extension inject a content script, and have the hosted site pass the auth details back to the Chrome extension through an event or something. Seems like a huge hack though and possibly subject to security flaws. Anyone else have ideas??
Although it was mentioned in the comments that this works with the Google auth provider using chrome.identity sadly there was no code example so I had to figure out myself how to do it.
Here is how I did it following this tutorial:
(It also mentions a solution for non-Google auth providers that I didn't try)
Identity Permission
First you need permission to use the chrome identity API. You get it by adding this to your manifest.json:
{
...
"permissions": [
"identity"
],
...
}
Consistent Application ID
You need your application ID consistent during development to use the OAuth process. To accomplish that, you need to copy the key in an installed version of your manifest.json.
To get a suitable key value, first install your extension from a .crx file (you may need to upload your extension or package it manually). Then, in your user data directory (on macOS it is ~/Library/Application\ Support/Google/Chrome), look in the file Default/Extensions/EXTENSION_ID/EXTENSION_VERSION/manifest.json. You will see the key value filled in there.
{
...
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgFbIrnF3oWbqomZh8CHzkTE9MxD/4tVmCTJ3JYSzYhtVnX7tVAbXZRRPuYLavIFaS15tojlRNRhfOdvyTXew+RaSJjOIzdo30byBU3C4mJAtRtSjb+U9fAsJxStVpXvdQrYNNFCCx/85T6oJX3qDsYexFCs/9doGqzhCc5RvN+W4jbQlfz7n+TiT8TtPBKrQWGLYjbEdNpPnvnorJBMys/yob82cglpqbWI36sTSGwQxjgQbp3b4mnQ2R0gzOcY41cMOw8JqSl6aXdYfHBTLxCy+gz9RCQYNUhDewxE1DeoEgAh21956oKJ8Sn7FacyMyNcnWvNhlMzPtr/0RUK7nQIDAQAB",
...
}
Copy this line to your source manifest.json.
Register your Extension with Google Cloud APIs
You need to register your app in the Google APIs Console to get the client ID:
Search for the API you what to use and make sure it is activated in your project. In my case Cloud Firestore API.
Go to the API Access navigation menu item and click on the Create an OAuth 2.0 client ID... blue button.
Select Chrome Application and enter your application ID (same ID displayed in the extensions management page).
Put this client ID in your manifest.json. You only need the userinfo.email scope.
{
...
"oauth2": {
"client_id": "171239695530-3mbapmkhai2m0qjb2jgjp097c7jmmhc3.apps.googleusercontent.com",
"scopes": [
"https://www.googleapis.com/auth/userinfo.email"
]
}
...
}
Get and Use the Google Auth Token
chrome.identity.getAuthToken({ 'interactive': true }, function(token) {
// console.log("token: " + token);
let credential = firebase.auth.GoogleAuthProvider.credential(null, token);
firebase.auth().signInWithCredential(credential)
.then((result) => {
// console.log("Login successful!");
DoWhatYouWantWithTheUserObject(result.user);
})
.catch((error) => {
console.error(error);
});
});
Have fun with your Firebase Service...

Specify custom redirect_uri in a web app that uses Azure AD authentication with oidc and a middleware

I am trying to authenticate an app with Azure AD. It's all good in localhost, it redirects to Azure AD where I enter details to authenticate, and it sends back the token that allows to view the resource.
Everything managed behind the scenes with the Microsoft.AspNetCore.Authentication.AzureAD.UI 3.1.10 in an aspnetcore 3.1 application.
My app runs on http://localhost:5000 and I can configure the redirectUri/replyUri at Azure AD for that application to support this url. All good.
The problem is in a different environment when my app runs in a service fabric cluster.
I can see the problem
AADSTS50011: The reply URL specified in the request does not match the reply URLs configured for the application
When I inspect the url I can see that the redirect_uri has some url like this http://12.12.12.12/signin-oidc
The problem is double here. First of all I don't know which IP the cluster is gonna assign. Second, it is http, not https, and that's not supported by Azure AD.
Luckily my app has an external Url with a reverse proxy I can use to access. Something like https://myservicefabriccluster.com/MyApp
That Url I could configure as my redirect_uri in both my application and Azure AD, but I don't know how to do so.
My code has something like this:
services
.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
where I bind my settings.
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"ClientId": "76245c66-354e-4a94-b34d-...",
"TenantId": "59c56bd4-ce18-466a-b515-..."
},
I can see the AzureADOptions supports some other parameters such as Domain (not needed) or CallbackPath (which by default is ok being /signin-oidc) but there is nothing similar to ReplyUrl or RedirectUri where I can specify an absolute URL as the callback.
I have found a few similar issues without an answer. Others suggest some kind of tricks like a middleware that rewrites that parameter just before redirecting to Azure AD.
Certainly there must be an easier way to deal with this problem that I expect is not so strange. Any help please?
The solution to overwrite redirect_uri parameter with a custom value is to use the Events available in OpenIdConnect library. This library should be available as it's a dependency for Microsoft.AspNetCore.Authentication.AzureAD.UI, so this is my solution that, in addition to the standard properties for AzureADOptions it adds a flag to determine whether the redirect uri must be overwritten and a value to do so. I hope it's self explanatory
services
.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => configuration.Bind("AzureAd", options));
var isCustomRedirectUriRequired = configuration.GetValue<bool>("AzureAd:IsCustomRedirectUriRequired");
if (isCustomRedirectUriRequired)
{
services
.Configure<OpenIdConnectOptions>(
AzureADDefaults.OpenIdScheme,
options =>
{
options.Events =
new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = async ctx =>
{
ctx.ProtocolMessage.RedirectUri =
configuration.GetValue<string>("AzureAd:CustomRedirectUri");
await Task.Yield();
}
};
});
}
services
.AddAuthorization(
options =>
{
options.AddPolicy(
PolicyConstants.DashboardPolicy,
builder =>
{
builder
.AddAuthenticationSchemes(AzureADDefaults.AuthenticationScheme)
.RequireAuthenticatedUser();
});
});
And the appsettings.json would have something like this:
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"ClientId": "76245c66-354e-4a94-b34d-...",
"TenantId": "59c56bd4-ce18-466a-b515-..."
"IsCustomRedirectUriRequired": true,
"CustomRedirectUri": "https://platform-cluster-development01.cubictelecom.com:19008/Scheduler/WebApi/signin-oidc"
},
Notice the IsCustomRedirectUriRequired and CustomRedirectUri are my custom properties that I read explicitly in order to overwrite (or not) the redirect uri query parameter when being redirected to the identity provider (i.e: Azure AD)
Looking at this, you should be configuring the public URL as the redirect URI, which is a value such as this:
https://myservicefabriccluster.com/MyApp
It looks like that the above library does not easily support this, and forces the redirect URI to be based on the HTTP listening URL of the code. As part of resolving this it is worth considering how you are writing your code:
This line of code indicates that your app is limited to only ever working with Azure AD:
- services.AddAzureAD
This line of code would ensure that your code works with both AzureAD and any other Authorization Server that meets the Open Id Connect standard:
- services.AddOpenIdConnect
The latter option also has an Events class with a commonly used OnRedirectToIdentityProvider operation that you can use to override the CallbackPath and provide a full RedirectUri.
Azure AD endpoints are standards based so you do not strictly have to use AzureAD specific libraries. Out of interest, I have a Single Page App Azure code sample that uses a neutral library like this, so I know this technique works.

Network Error in Blazor WebAssembly site with ASP.Net Core Identity login link

In my BlazorWebAssembly + ASP.NET Core Identity test site (.NET 5.0 RC1), I'm getting the following error when trying to log in.
There was an error trying to log you in: 'Network Error'
I have already set appsettings OIDC to be the following:
{
"SiteName": "MyProject",
"oidc": {
"Authority": "https://167.172.118.170/",
"ClientId": "MyProject",
"DefaultScopes": [
"openid",
"profile"
],
"PostLogoutRedirectUri": "/",
"RedirectUri": "https://167.172.118.170/authentication/login-callback",
"ResponseType": "code"
}
}
Why is it not able to connect?
Test site is at http://167.172.118.170/ and the code can be found in https://github.com/jonasarcangel/BlazorLoginNetworkErrorIssue
It is clear by now that Blazor uses the internal url http://localhost:5008 as the authority instead of the external url http://167.172.118.170/
When the client attempts to connect to http://localhost:5008/.well-known/openid-configuration, an error occurs: connection refused...
As a matter of fact the client should use this url: http://167.172.118.170/.well-known/openid-configuration, but it does not as the value of the authority is determined by Blazor.
If you type the url http://167.172.118.170/.well-known/openid-configuration in the browser's address bar, you'll see all the configuration information about the Identity Provider. Indeed, http://167.172.118.170/ is the authority. But as you've seen setting the Authority to this url in the appsettings.json file was simply ignored, and the internal url was used instead.
How to solve this ? We should tell Blazor not to use the internal url but the external one...
Attempts suggested:
In the web server project's Startup class's ConfigureService change this code:
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationIdentityDbContext>
();
To
services.AddIdentityServer(options =>
{
options.IssuerUri = "https://167.172.118.170/";
})
.AddApiAuthorization<ApplicationUser,
ApplicationIdentityDbContext>();
Use the ForwardedHeaders middleware. See this sample as to how to do it.
Stick to the above... The issue is here, and not somewhere else.
Good luck...
I just test and the url http://167.172.118.170/_configuration/BlazorWorld.Web.Client returns
{
"authority": "http://localhost:5008",
"client_id": "BlazorWorld.Web.Client",
"redirect_uri": "http://localhost:5008/authentication/login-callback",
"post_logout_redirect_uri": "http://localhost:5008/authentication/logout-callback",
"response_type": "code",
"scope": "BlazorWorld.Web.ServerAPI openid profile"
}
Then the app try to connect to http://localhost:5008/.well-known/openid-configuration :
So the deployed appsettings is probably not the good one.

Securing ASP.NET Core Web API with Azure AD

I am trying to start a new project that uses Azure AD for authentication. It is set up so that I have a SPA on the front end that gets information from an ASP.NET core web API, both of which I am creating. I am having trouble getting the front end token to authorize in the API. Every time I send a request to the API I get the error: Microsoft.IdentityModel.Tokens.SecurityTokenInvalidAudienceException: IDX10231: Audience validation failed.
I have set up the project as following.
In Azure AD I have set up two applications: One for the front end and one for the API. The API application has an API exposed called access_as_user. The front end application then has access to this. I have also made a client secret for both and added redirect URL's for the front end.
In my ASP.NET core API I am using I'm using Microsoft.Identity.Web and calling it like so:
// startup.cs
...
public void ConfigureServices(IServiceCollection services)
{
...
services.AddProtectedWebApi(Configuration, subscribeToJwtBearerMiddlewareDiagnosticsEvents: true);
...
}
...
In my config the values are as follows:
"AzureAD": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "mydomain.onmicrosoft.com",
"TenantId": "*MY TENANT ID*",
"ClientId": "*Client ID of API",
"ClientSecret": "Client Secret for API",
"Audience": "Client ID of Front End"
}
To get auth I followed this tutorial -> here <- to set up PostMan to use OAuth 2.0 and get the tokens for me automatically. The magic happens at the end of step 3 in the tutorial.
Any help would be greatly appreciated.
Edit: After following the tutorial like alphaz18 suggested, I found my issue. I had forgotten to add the Authentication middle ware in the Configure part of Startup.cs.
app.UseRouting();
app.UseAuthentication(); // This line was missing.
app.UseAuthorization();
I would highly recommend you follow the Microsoft sample tutorials first as they are all working. they give you all steps to get these samples working and is a great place to start:
https://github.com/Azure-Samples/ms-identity-javascript-angular-spa-aspnetcore-webapi
in that tutorial you posted, I don't see anything about audience either. So where did you get that from?

code for using O365 groups in a .net core app

I am working on a .net core app and have to integrate O365 security groups for roles assignment, does someone have sample code to share, will be very helpful.
I have already used Azure AD app registration concept for O365 authentication and its working perfectly. .Net core app is hosted on IIS, when accessed by typing in url in browser, it redirects users to login.microsoftonline.com, once authenticated, users then see dashboard part of .net core app.
Not so sure about how O365 groups can be used in .net core app for permissions management, so looking for some sample snippet, thanks in advance.
You can query graph api either as your app or impersonate the user, to read which groups the user is in and then use those Id to filter views or what ever you need to do.
you can use the "List memberOf"
https://learn.microsoft.com/en-us/graph/api/user-list-memberof?view=graph-rest-1.0
Hope it helps.
Office365 security groups can be used for permissions management in your app, by verifying if a user is a member of a security group. You can achieve that by using Microsoft Graph API as MohitVerma suggested.
First, define groups to roles mapping in your app (configuration file seems to be a good place for that). Each group has a unique id, which you can get using e.g. Office365 or Microsoft Graph and map to a custom role in your config.json file:
{
"AppRoles": [
"Admin": "d17a5f86-57f4-48f8-87a0-79761dc8e706",
"Manager": "9a6a616e-5637-4306-b1fe-bceeaa750873"
]
}
Then, after successful login to the app, call the Graph API to get all groups the user belongs to. You will get a list of groups, each containing id property:
GET https://graph.microsoft.com/v1.0/me/memberOf
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#directoryObjects",
"value": [
{
"#odata.type": "#microsoft.graph.directoryRole",
"id": "43a63cc2-582b-4d81-a79d-1591f91d5558",
"displayName": "Company Administrator",
"roleTemplateId": "62e90394-69f5-4237-9190-012177145e10"
},
{
"#odata.type": "#microsoft.graph.group",
"id": "d17a5f86-57f4-48f8-87a0-79761dc8e706",
"createdDateTime": "2017-07-31T17:36:25Z",
"displayName": "Admins group",
"securityEnabled": true
}
]
}
You can use MS Graph SDK for .NET to make a request and to create a group objects form the response:
var userGroups = await graphServiceClient.Me.Groups.Request().GetAsync();
Finally, verify the id of each group with your custom roles, e.g.:
public string GetRole(IEnumerable<Group> userGroups, IConfiguration config)
{
foreach (var group in userGroups)
{
switch (group.id)
{
case config.GetSection("AppRoles:0"):
return "Admin";
case config.GetSection("AppRoles:1"):
return "Manager";
default:
return "Unknown";
}
}
}
Make sure to grant permissions for your app to access Microsoft Graph.

Resources