Azure AD Authentication and authorization - authentication

I am developing Asp.net MVC project, This app authenticating form Azure AD but problem with role based authorization, action not authorized base in the role group. I put my code here, please review and help me. Contact action not authorizing, I created Operator1 group of security type and assigned to user
public partial class Startup {
private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
private static string appKey = ConfigurationManager.AppSettings["ida:ClientSecret"];
private static string aadInstance = EnsureTrailingSlash(ConfigurationManager.AppSettings["ida:AADInstance"]);
private static string tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
public static readonly string Authority = aadInstance + tenantId;
// This is the resource ID of the AAD Graph API. We'll need this to request a token to call the Graph API.
string graphResourceId = "https://graph.windows.net";
public void ConfigureAuth(IAppBuilder app) {
ApplicationDbContext db = new ApplicationDbContext();
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions {
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications() {
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
AuthorizationCodeReceived = (context) => {
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new ADALTokenCache(signedInUserID));
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCodeAsync(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId).Result;
return Task.FromResult(0);
}
}
});
}
}

Look into this sample below which helps you add authorization using app roles & roles claims to an ASP.NET Core web app that's signs-in users with the Microsoft identity platform.
Learn more here:
https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/master/5-WebApp-AuthZ/5-1-Roles

My code is working for me just I was needed to register app as Enterprise Application.

Related

How to convert an MS Graph API call to GraphServiceClient method call?

What is the GraphServiceClient version of querying another user's calendar?
var cal = await _graphServiceClient.???? (see code below)
Startup code
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
var app = builder.Build();
Razor pages code
using Microsoft.Graph;
using Microsoft.Identity.Web;
namespace MSGraphAPIPOC.Pages;
[Authorize]
[AuthorizeForScopes(Scopes = new[] { "Calendars.ReadWrite" })]
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
private readonly GraphServiceClient _graphServiceClient;
public IndexModel(ILogger<IndexModel> logger, GraphServiceClient graphServiceClient)
{
_logger = logger;
_graphServiceClient = graphServiceClient;
}
public async Task OnGet()
{
...
https://graph.microsoft.com/v1.0/users/<calendarSMTP>/calendarview?startDateTime=2022-07-05T00:00:00&endDateTime=2022-07-05T23:59:00&select=start,end,subject
var cal = await _graphServiceClient.???? //What is the equivalent of the api call above?
...
}
}
Any help would be appreciated.
There are 2 types of ms graph api permission, one is delegate which means users can sign in first and then query their own information via ms graph api. Another type is Application, this means application can query all users' information via graph api.
Come back to your scenario, you integrate azure ad into your asp.net core web application, which means users have to sign in first before then visit Index page right? So you are now using the delegate api permission, which allowing you to use await _graphServiceClient.Me.CalendarView.Request( queryOptions ).GetAsync() to query his/her own calendar view but don't have permission to query others.
If you want to query others, you have to consent application api permission. In your scenario, the api supports application permission. Then following the screenshot in this section to add the permission. Then using code below or this sample section to call the api:
using Azure.Identity;
using Microsoft.Graph;
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "your_tenant_name.onmicrosoft.com";
var clientId = "azure_ad_app_id";
var clientSecret = "client_secret";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var queryOptions = new List<QueryOption>()
{
new QueryOption("startDateTime", "2022-07-05T00:00:00"),
new QueryOption("endDateTime", "2022-07-05T23:59:00")
};
var res = await graphClient.Users["user_id"].CalendarView.Request(queryOptions).Select("start,end,subject").GetAsync();

Getting 401 from custom Api when using Microsoft.Identity.platform to protect api

I am following the tutorial from Microsfot.document for how to protect api using Azure AD (Microsoft Identity).
The steps I took are following: Sorry I tried to put information that might be helpful but too much to get to the issue most of the time contributors ask for screenshot or the code.
I followed several documents and video tutorials but here is the link for one of them: https://learn.microsoft.com/en-us/learn/modules/identity-secure-custom-api/2-secure-api-microsoft-identity
WebApi.
Created a webapi using core 5. Register it in Azure AD.
Created single scope 'check' and allowed permission to user and admin.
Webapp
Created webapp using .net(classic) Note that webapi is core 5.
Created a webapp and register it in Azure AD.
Setup the authentication and created a OnAuthorizationCodeReceived to get the access token to call the api.
Configuration:
1.From Azure AD->Registration for Webapi-> selected application(web app created above) and give permission to the scope.
2. For Azure AD->Registration for webapp-> Access permission->delegate->selected the scope.
Test:
1.Run the test. At this point; I do not have [Authorization] on the api method which I am calling.
2. Webapp successfully able to get the string which is returned by the api. Somewhat I get the idea that plumbing was right.
Added [Authorize] filter on the app method.
Result 401 unauthorized.
I have checked multiple times and looked at multiple tutorial and rewrote my code, watched several videos and updated my code but I am always getting 401 error.
Below is the code;
Api controller:
namespace Utility.Controllers
{
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class AzAdUtility : ControllerBase
{
// GET: api/<AzAdUtility>
[HttpGet]
public string Get()
{
//HttpContext.VerifyUserHasAnyAcceptedScope(new string[] {"check"});
var name = "Vaqas";
return name;
}
}
}
Api startup :
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"));
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "GlobalNetApiUtility", Version = "v1" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Utility v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Api Appsettings:
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "myorg.onmicrosoft.com",
"ClientId": "abcd.............................",
"TenantId": "dabcd.............................."
},
Webapp startup:
Only adding startup page because at first all I am doing getting some data for testing purpose in the OnAuthorizationCodeReceived.
public class Startup
{
// The Client ID is used by the application to uniquely identify itself to Azure AD.
static string clientId = System.Configuration.ConfigurationManager.AppSettings["ClientId"];
// RedirectUri is the URL where the user will be redirected to after they sign in.
string redirectUri = System.Configuration.ConfigurationManager.AppSettings["RedirectUri"];
// Tenant is the tenant ID (e.g. contoso.onmicrosoft.com, or 'common' for multi-tenant)
static string tenant = System.Configuration.ConfigurationManager.AppSettings["Tenant"];
// Authority is the URL for authority, composed by Microsoft identity platform endpoint and the tenant name (e.g. https://login.microsoftonline.com/contoso.onmicrosoft.com/v2.0)
string authority = String.Format(System.Globalization.CultureInfo.InvariantCulture, System.Configuration.ConfigurationManager.AppSettings["Authority"], tenant);
//string authority = "https://login.microsoftonline.com/" + tenant + "/adminconsent?client_id=" + clientId;
string clientSecret = System.Configuration.ConfigurationManager.AppSettings["ClientSecret"];
/// <summary>
/// Configure OWIN to use OpenIdConnect
/// </summary>
/// <param name="app"></param>
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Sets the ClientId, authority, RedirectUri as obtained from web.config
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
// PostLogoutRedirectUri is the page that users will be redirected to after sign-out. In this case, it is using the home page
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
// ResponseType is set to request the code id_token - which contains basic information about the signed-in user
//ResponseType = OpenIdConnectResponseType.CodeIdToken,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
// OpenIdConnectAuthenticationNotifications configures OWIN to send notification of failed authentications to OnAuthenticationFailed method
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed
}
}
);
}
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification notification)
{
notification.HandleCodeRedemption();
var idClient = ConfidentialClientApplicationBuilder.Create(clientId)
.WithRedirectUri(redirectUri)
.WithClientSecret(clientSecret)
.WithAuthority(authority)
.Build();
try
{
var apiScope = "api://28......................../check2 api://28................/check api://28...........................1d/AzAdUtility.Get";
string[] scopes = apiScope.Split(' ');
//gettig the token no issues.
var result = await idClient.AcquireTokenByAuthorizationCode(
scopes, notification.Code).ExecuteAsync();
var myurl = "https://localhost:99356/api/AzAdUtility";
var client = new HttpClient();
// var accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(Constants.ProductCatalogAPI.SCOPES);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken); //accessToken
var json = await client.GetStringAsync(myurl);
var serializerOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
//getting 401 response
var checkResponse = JsonSerializer.Deserialize(json, typeof(string), serializerOptions) as string;
}
catch (Exception ex)
{
string message = "AcquireTokenByAuthorizationCodeAsync threw an exception";
notification.HandleResponse();
notification.Response.Redirect($"/Home/Error?message={message}&debug={ex.Message}");
}
}
/// <summary>
/// Handle failed authentication requests by redirecting the user to the home page with an error in the query string
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
context.HandleResponse();
context.Response.Redirect("Error/AccessDenied/?errormessage=" + context.Exception.Message);
return Task.FromResult(0);
}
}
In Api startup class I was missing app.UseAuthentication().
I never really thought that would be an issue. Once I added this code. I got the expected response rather than 401 Unauthorized error

How to automatically login Okta user with active login session to .NET 4.5 Web Forms app

I've got a .NET 4.5 Web Forms app with Okta authentication on top. The authentication setup seems to be working fine; I can login and logout and get my Okta user info/claims from the context variable.
What I'd like to do is detect on page load whether a user already has an active Okta session in their browser and then log them into the application. Or if they don't have a session do nothing and stay on the application page.
Making a challenge call to the authentication manager
HttpContext.Current.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "/Login.aspx" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
almost does what I want. If the user has an active session it'll do some redirects and log them in. But if they're not logged in they get sent to, and left on, the Okta login page. Which is not what I want.
I thought I would be able to access some cookies that Okta sets when a user logs in via an Okta page, but when checking through the browser debugger and checking Request.Cookies they don't seem to be available at that stage. And the context doesn't have access to the user info either.
edit: Also, if it helps, this is what my Startup.cs looks like
using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;
using IdentityModel.Client;
using Microsoft.AspNet.Identity;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using System.Collections.Generic;
using System.Configuration;
using System.Security.Claims;
[assembly: OwinStartup(typeof(WebApplication.Startup))]
namespace WebApplication
{
public class Startup
{
// These values are stored in Web.config. Make sure you update them!
private readonly string _clientId = ConfigurationManager.AppSettings["okta:ClientId"];
private readonly string _redirectUri = ConfigurationManager.AppSettings["okta:RedirectUri"];
private readonly string _authority = ConfigurationManager.AppSettings["okta:OrgUri"];
private readonly string _clientSecret = ConfigurationManager.AppSettings["okta:ClientSecret"];
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
public void ConfigureAuth(IAppBuilder app)
{
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = _clientId,
ClientSecret = _clientSecret,
Authority = _authority,
RedirectUri = _redirectUri,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
Scope = "openid profile email offline_access",
TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name" },
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
// Exchange code for access and ID tokens
var tokenClient = new TokenClient($"{_authority}/v1/token", _clientId, _clientSecret);
var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(n.Code, _redirectUri);
if (tokenResponse.IsError)
{
throw new Exception(tokenResponse.Error);
}
var userInfoClient = new UserInfoClient($"{_authority}/v1/userinfo");
var userInfoResponse = await userInfoClient.GetAsync(tokenResponse.AccessToken);
var claims = new List<Claim>(userInfoResponse.Claims)
{
new Claim("code", n.Code),
new Claim("id_token", tokenResponse.IdentityToken),
new Claim("refresh_token", tokenResponse.RefreshToken),
new Claim("access_token", tokenResponse.AccessToken)
};
n.AuthenticationTicket.Identity.AddClaims(claims);
},
},
});
}
}
}

How to generate token for WebAPI who is hosted on Azure AD?

I have one api which is hosted on azure AD.
I have below code inside Startup.cs
public partial class Startup
{
private static readonly string ClientId = ConfigurationManager.AppSettings["ida:ClientId"];
private static readonly string AadInstnace = ConfigurationManager.AppSettings["ida:AADInstance"];
private static readonly string TenantId = ConfigurationManager.AppSettings["ida:TenantId"];
private static readonly string PostLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
private static readonly string Authority = AadInstnace + TenantId;
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = ClientId,
Authority = Authority,
PostLogoutRedirectUri = PostLogoutRedirectUri
});
}
}
I do not see any postback token generation code here :(
how can I get a token which i can use to call this webapi from console app ?
Have a look at nuget package - Microsoft.IdentityModel.Clients.ActiveDirectory (https://www.nuget.org/packages/Microsoft.IdentityModel.Clients.ActiveDirectory)
You can then generate an access token using code along the line of,
var authority = "https://login.microsoftonline.com/your-aad-tenant-id/oauth2/token";
var context = new AuthenticationContext(authority);
var resource = "https://some-resource-you-want-access-to";
var clientCredentials = new ClientCredential(clientId, clientSecret);
var result = await context.AcquireTokenAsync(resource, clientCredentials);
You will need to create the secret value for the AAD clientId

Restrict user from opening new tab in MVC application

How to restrict a user from opening a new tab in mvc application, we are using azure active directory authentication so by default it uses cookie & we can't disable cookie.
In order to store data we are using session to store class details and its persisted throughout the application however a new tab creates an issue as the session details are getting shared, what we can do to resolve this issue any code link will surely help.
code using AAD
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType); // Authentication type is cookies
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = (context) =>
{
X509Certificate2 cert = null;
X509Store store = new X509Store(StoreLocation.CurrentUser);
// Here the code all about certification & access token
return Task.FromResult(0);
}
}
});
}