Can we use Azure AD and issue our own token to client side? - azure-active-directory

i have questions. Im using azure ad for my front end and backend. Front end using angular. The FE will connect to azure AD and pass the token to our api. Our api will validate the token from FE.then i will check the user email from azure ad. If user not exist in our database. I will add a new one to my db. So right now i dont want to use role management inside the azure ad. It is possible i issue another token by my own instead of using the azure ad? The problem is, when i want to authorize my api using role table in my db. I cant simply [Authorize("Admin")] because this one will check the roles claims inside azure token. I want to use my roles in my db instead. So other solution i can think is create custom authorize attribute but this might not best solution because it will search the user role in db every request. So now if someone have experience like my problem feel free to give ur suggestion. Thank you

Check this thread, you can add custom claims in OnTokenValidated of OIDC event:
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Events = new OpenIdConnectEvents
{
OnTokenValidated = ctx =>
{
// add claims
var claims = new List<Claim>
{
new Claim(ClaimTypes.Role, "Admin")
};
var appIdentity = new ClaimsIdentity(claims);
ctx.Principal.AddIdentity(appIdentity);
return Task.CompletedTask;
},
};
});
Then, you could create Claims-based authorization or Policy-based authorization, and you could also create Custom Authorization attributes to do the Authentication.

Related

Authorization on a Blazor Server app using Microsoft Identity (Azure AD Authentication) doesn't work

Forgive me if this is an obvious question with an easy answer but for the life of me I cannot get my app to behave the way I want.
When I normally use MS Identity in my Blazor apps I can create roles and policies which all come from a SQL database. For this B2B app I need to use Azure AD and the groups within there to authenticate and authorize access.
At the moment the whole app is secured because the default policy is being applied to all parts of the site but I would like to use [Authorize(Policy = "ViewCustomer")] for example to ensure users have the right permission to view a particular part.
I am sure that this part of my program.cs is missing policies and is part of the problem:
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = options.DefaultPolicy;
});
Trouble is I don't have a clue how to create these so they refer to groups (or similar) in the Azure AD tenant. My complete program.cs is:
using DevExpress.Blazor;
using DataModel.Models;
using Microsoft.EntityFrameworkCore;
using BlazorUI.Hubs;
using BlazorUI.Services;
using Xero.NetStandard.OAuth2.Config;
using BlazorUI.Services.Interfaces;
using DataModel.Xero.Interface;
using DataModel.DickerData.Interfaces;
using DataModel.DickerData;
using DataModel.Xero;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
var builder = WebApplication.CreateBuilder(args);
var initialScopes = builder.Configuration["DownstreamApi:Scopes"]?.Split(' ') ?? builder.Configuration["MicrosoftGraph:Scopes"]?.Split(' ');
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
.AddMicrosoftGraph(builder.Configuration.GetSection("MicrosoftGraph"))
.AddInMemoryTokenCaches();
builder.Services.AddControllersWithViews()
.AddMicrosoftIdentityUI();
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = options.DefaultPolicy;
});
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor()
.AddMicrosoftIdentityConsentHandler();
builder.Services.AddScoped<ISettingService, SettingService>();
builder.Services.AddScoped<IXeroService, XeroService>();
builder.Services.AddScoped<IDickerDataService, DickerDataService>();
//XERO SETTINGS
builder.Services.Configure<XeroConfiguration>(builder.Configuration.GetSection("XeroConfiguration"));
//DICKER DATA SETTINGS
builder.Services.Configure<DickerConfig>(builder.Configuration.GetSection("DickerDataConfiguration"));
//DEVEXPRESS
builder.Services.AddDevExpressBlazor(configure => configure.BootstrapVersion = BootstrapVersion.v5);
//ENTITY FRAMEWORK
builder.Services.AddDbContextFactory<ApplicationDBContext>(options =>
{
options.UseSqlServer(builder.Configuration.GetConnectionString("DBConnection"));
options.EnableSensitiveDataLogging();
});
var app = builder.Build();
//DEVEXPRESS
builder.WebHost.UseWebRoot("wwwroot");
builder.WebHost.UseStaticWebAssets();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
//REGISTER SIGNAL R HUBS
app.MapHub<MessageHub>(MessageHub.PATHTOHUB);
app.Run();
Thank you so much to anyone that may be able to enlighten me.
You can add app roles to your application, assign them to selected users and obtain them as claims so they can be used in tandem with your AuthorizeAttribute.
Alternatively, you can further customize or augment the authorization criteria using Claim Based Authorization in tandem with your policy.

ASP.NET 6 WebAPI Authentication with SSO

I have an ASP.NET 6.0 Web API project. I would like to add authentication and authorization to it, but it must use SSO via Azure.
We already have a SPA application that does this, it uses the Angular MSAL library to redirect the user to an SSO Login page, then returns to the SPA with an access token. The access token is then added to the header of each request to the Web API, which uses it to enforce authentication.
Now we want to share our web API with other teams within our organization, and we would like to have that login process just be another API call, rather than a web page.
Conceptually, a client would hit the /login endpoint of our API, passing in a userID and password. The web API would then get an access token from Azure, then return it as the payload of the login request. It's then up to the client to add that token to subsequent request headers.
I have done this with regular ASP.NET Identity, where all of the user and role data is stored in a SQL database, but since our organization uses SSO via Azure Active Directory, we would rather use that.
I have researched this topic online, and so far all of the examples I have seen use a separate SPA, just like we already have. But as this is a web api, not a front-end, we need to have an API method that does this instead.
Is this even possible? I know Microsoft would rather not have user credentials flow through our own web server, where a dishonest programmer might store them for later misuse. I understand that. But I'm not sure there's a way around this.
Thanks.
I believe you are looking for the Resource Owner Password (ROP) flow. You can use IdentityModel.OidcClient to implement it.
Sample code:
public class Program
{
static async Task Main()
{
// call this in your /login endpoint and return the access token to the client
var response = await RequestTokenAsync("bob", "bob");
if (!response.IsError)
{
var accessToken = response.AccessToken;
Console.WriteLine(accessToken);
}
}
static async Task<TokenResponse> RequestTokenAsync(string userName, string password)
{
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync(Constants.Authority);
if (disco.IsError) throw new Exception(disco.Error);
var response = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "roclient",
ClientSecret = "secret",
UserName = userName,
Password = password,
Scope = "resource1.scope1 resource2.scope1",
Parameters =
{
{ "acr_values", "tenant:custom_account_store1 foo bar quux" }
}
});
if (response.IsError) throw new Exception(response.Error);
return response;
}
}
Sample taken from IdentityServer4 repository where you can find more ROP flow client examples.
I would recommend that you don't go with this implementation and instead have all clients obtain their access tokens directly from Azure AD like you did with your Angular SPA.

How to use Azure AppRoles in Blazor Server with Azure Active Directory

I have an up and running .NET 5 Web-API with a Blazor Server Client in Production. I'd like to switch from Individual User Accounts to Azure AD using App Roles to authenticate specific Users in my Controllers. I found lots of Information regarding Webassembly but none for Blazor Server.
Has somebody a working Solution for a .NET 5/6 Web-Api with a Blazor Server Client and integrating Azure App Roles?
Apps are already registered in the Azure Portal and so forth, I just need to know how to pass the App Roles specific stuff to my API, so my Controller can work with the [Authorize("Admin")] stuff. I suspect it will use Bearer Tokens aswell.
Edit:
Thanks a lot for reading. So I figured out that if I use something like this in my Controller only using the [Authorize] Attribute without any roles:
var identities = HttpContext.User.Identities.ToList();
foreach (var item in identities)
{
if (item.RoleClaimType == "admin")
{
// return or do something
}
}
It would just work fine but there has to be some smoother solution for this or am I doing this completly wrong? When I look at the WASM Samples, they pick up the AppRoles with their token and the Controller simply can use the [Authorize(Roles = "xyz")] Attribute. What am I missing here? :/
Btw, this is how my Program.cs looks right now:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(options =>
{
builder.Configuration.Bind("AzureAd", options);
options.TokenValidationParameters.RoleClaimType =
"admin";
options.TokenValidationParameters.RoleClaimType = "doku";
},
options => { builder.Configuration.Bind("AzureAd", options); });
Thank you guys/gals <3
Please check if the given references are of use in your case.
A SERVER API app can authorize users to access secure API endpoints with authorization policies for security groups, AAD Administrator Roles, and App Roles
In Program.cs of a SERVER app, specify the claim as roleclaim
example:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(options =>
{
Configuration.Bind("AzureAd", options);
options.TokenValidationParameters.RoleClaimType =
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role";
},
options => { Configuration.Bind("AzureAd", options); });
Then you can use admin role on authorization controller to access
[Authorize(Roles = "admin")]
Here in App roles section you can see the configuration for both
server and client.
Edit the app role in the manifest editor in portal and then give
proper api permissions , expose scopes and grant permission for admin
consent >see Add app roles and get them from a token .And the
procedural logic must contain those scopes required by api.
Note : The appRoles manifest property of both the client and the
server Azure portal app registrations must include the same configured
roles.
Please check this for more detailed information which guides for both server and client apps.
Other references:
using-app-roles-with-AAD-blazor-server-client scenario | codemaze.com
quickstart-configure-app-expose-web-apis
quickstart-configure-app-access-web-apis

How to add a claim in the bearer send by httpinterceptor of adal-angular

In a angularjs application, i use adal and adal-angular libraries to authentify user on Azure AD. On backend I use OWIN middleware in my webAPI to add custom claims with :
app.UseWindowsAzureActiveDirectoryBearerAuthentication(new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = ConfigurationManager.AppSettings["ida:Audience"],
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
Provider = new OAuthBearerAuthenticationProvider()
{
OnValidateIdentity = async context =>
{
if(!context.IsValidated)
return;
var userManager = context.OwinContext.Get<UserManager>();
string email = context.Ticket.Identity.GetClaimValue(System.Security.Claims.ClaimTypes.Email);
User user = userManager.GetByEmail(email);
context.Ticket.Identity.AddClaim(new Claim(ClaimTypes.UserId, user.Id.ToString(CultureInfo.InvariantCulture));
}
}
});
It's work for the current request, but how to add the claim in the bearer send by httpinterceptor of adal-angular for the next requests?
To answer your direct question: you cannot modify the access token, period.
It has a digital signature which will no longer be valid if you change anything in the token.
It's a security measure that prevents tampering.
Your OWIN middleware downloads the public keys of the signing key pairs from Azure AD on startup, and uses those to validate tokens.
Now if you stored your internal ids as extension attributes,
it would actually be possible to tell Azure AD to include that in the tokens.
This feature is in preview though, and not recommended for production use: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-optional-claims#configuring-custom-claims-via-directory-extensions.
Here you would set the internal id as an extension on the User entity, and have it included for access tokens to your API.
A pragmatic solution to your issue might be an in-memory cache.
How often do emails change?

Identity Server 3 AzureAd Claims not returning

I have a working Identity Server application, and I'm setting it up to work with Azure AD. I've got my Azure Ad App registration and I can authenticate with it properly.
Looking at this and trying to do something similar to store the 3rd party user IDs associated with a user, but I'm not getting the sub or nameIdentifier claims back from AAD.
Do I need to request these from AzureAD somehow? - Their docs seem to be to be saying that the "sub" claim is (or at least can be) returned: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-token-and-claims#_subject. This article seems to say that sub isn't returned, but it's for multitenant applications, so I'm not too sure if that's relevant.
I'm sure I'm missing something simple, but can't find anything relevant on Google.
Cheers,
Alex
The article Work with claims-based identities is too old and this Azure AD token reference article should be right about the token claims in the token issued by Azure AD.
Based on the test, I could get the sub claim from Azure AD and it also issued by the IdentityServer3 like figure below:
Here is the code I configed for the IdentityServer3 for your reference:
var webApp = WebApp.Start("https://localhost:44333", app =>
{
app.UseIdentityServer(new IdentityServerOptions
{
SiteName = "NDC Demo",
SigningCertificate = cert,
Factory = factory,
AuthenticationOptions = new AuthenticationOptions
{
IdentityProviders = ConfigureAdditionalIdentityProviders,
EnableAutoCallbackForFederatedSignout = true
}
});
});
public static void ConfigureAdditionalIdentityProviders(IAppBuilder app, string signInAsType)
{
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "aad",
Caption = "Azure AD",
SignInAsAuthenticationType = signInAsType,
Authority = "https://login.microsoftonline.com/{tenantId}",
ClientId = "{clientIdFromAzurePortal}",
RedirectUri = "{redirectUri}",
});
}
If you still have the problem, would you mind sharing the request to Azure AD which you can capture it using Fiddler.

Resources