Add Claims (asp.net core mvc OpenID Owin Katana Authentication ) - azure

I am following this tutorial link. I am able login with azure ad user. but once the user gets authenticated. we want to store it into Identity claims for authentication.
We are migrating Asp.net MVC application into asp.net core MVC 1.0. In Asp.net MVC application we are adding the claims like this
context.AuthenticationTicket.Identity.AddClaim(new System.Security.Claims.Claim("urn:Projectname:access_token", result.AccessToken, XmlSchemaString, "Projectname"));
I want to know how to add the claims identity in the above tutorial.
Code Snippet
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
ClientId = clientId,
ClientSecret = clientSecret,
Authority = authority,
CallbackPath = Configuration["AzureAd:AuthCallback"],
ResponseType = OpenIdConnectResponseType.CodeIdToken,
PostLogoutRedirectUri = "/signed-out",
Events = new OpenIdConnectEvents()
{
OnAuthorizationCodeReceived = async context =>
{
var request = context.HttpContext.Request;
var currentUri = UriHelper.BuildAbsolute(request.Scheme, request.Host,request.PathBase, request.Path);
var credential = new ClientCredential(clientId, clientSecret);
var authContext = new AuthenticationContext(authority, AuthPropertiesTokenCache.ForCodeRedemption(context.Properties));
var result = await authContext.AcquireTokenByAuthorizationCodeAsync(
context.ProtocolMessage.Code, new Uri(currentUri), credential, resource);
// In result variable , we are getting the AccessToken and we want to add this into claims identity here.
context.HandleCodeRedemption();
}
}
});
Update
we are storing tokens,domain name ( Getting it from DB), Tenant Info for middle layer Authentication. Like in very controller action methods, we are getting the stored info from claims.
Something like that(Old Asp.net MVC Application code).
In Startup.Auth.cs class
In All controller action methods
We are migrating Asp.net MVC application into asp.net core MVC 1.0. So is there any equivalent method in asp.net core for adding the claims. I am following This sample. I am able login with azure ad user. but once the user gets authenticated. we want to store it into Identity claims for authentication(middle layer).

The Code
ClaimsPrincipal claimsPrincipal = await TransformClaims(context.Ticket.Principal, result);
context.Ticket = new AuthenticationTicket(
claimsPrincipal,
context.Ticket.Properties,
context.Ticket.AuthenticationScheme);
TransformClaims method Something like that
private Task<ClaimsPrincipal> TransformClaims(ClaimsPrincipal principal, AuthenticationResult result)
{
if (principal.Identity.IsAuthenticated)
{
// get this from cache or db
var nickname = "Nanu";
(principal.Identity as ClaimsIdentity).AddClaim(new Claim("Nickname", nickname));
(principal.Identity as ClaimsIdentity).AddClaim(new Claim("urn:innubex:access_token", result.AccessToken));
}
return Task.FromResult(principal);
}
Access the claims
string accesstoken = "", Nickname = "";
var claimsIdentity = User.Identity as ClaimsIdentity;
if (claimsIdentity.IsAuthenticated)
{
accesstoken = claimsIdentity.FindAll("urn:access_token").FirstOrDefault().Value;
Nickname = claimsIdentity.FindAll("Nickname").FirstOrDefault().Value;
}

This is how I was able to login using Claims Identity:
using System.Security.Claims;
private void registerLogin(Person person)
{
var userClaims = new List<Claim>
{
new Claim(ClaimTypes.Name, person.LoginName),
new Claim(ClaimTypes.GivenName, person.FirstName),
new Claim(ClaimTypes.Surname, person.LastName),
new Claim(ClaimTypes.Email, person.Email)
};
var principal = new ClaimsPrincipal(new ClaimsIdentity(userClaims, "local"));
Context.Authentication.SignInAsync("PutNameHere", principal);
}

Related

Resource Graph query using Azure Function .NET and User managed Identity?

In the example the DotNet-ResourceGraphClient requires ServiceClientCredentials. I do not know how to use a user-assigned-managed-identity directly.
For instance:
var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = umiClientId });
ResourceGraphClient argClient = new ResourceGraphClient(serviceClientCreds);
results in: Argument 1: cannot convert from 'Azure.Identity.DefaultAzureCredential' to 'Microsoft.Rest.ServiceClientCredentials'.
I found a PHP-example with credentials = MSIAuthentication(). Can anyone provide a similar example for dotnet-azure-resource-graph-sdk?
Thanks
To acquire a token credential for your code to approve calls to Microsoft Graph, one workaround is to utilize the ChainedTokenCredential, ManagedIdentityCredential and EnvironmentCredential classes.
The following snippet generates the authenticated token credential and implements those to the creation of a service client object.
var credential = new ChainedTokenCredential(
new ManagedIdentityCredential(),
new EnvironmentCredential());
var token = credential.GetToken(
new Azure.Core.TokenRequestContext(
new[] { "https://graph.microsoft.com/.default" }));
var accessToken = token.Token;
var graphServiceClient = new GraphServiceClient(
new DelegateAuthenticationProvider((requestMessage) =>
{
requestMessage
.Headers
.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
return Task.CompletedTask;
}));
REFERENCES:
Access Microsoft Graph from a secured .NET app as the app
Tutorial: Access Microsoft Graph from a secured .NET app as the app
thanks for the input.
Authentication with user managed identity.
https://learn.microsoft.com/en-us/dotnet/api/overview/azure/service-to-service-authentication#connection-string-support
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
// Connect client with user assigned managed identity.
string umiClientId = "<your-user-assigned-managed-identity-client-id>";
string conStrOpts = string.Format("RunAs=App;AppId={0}", umiClientId);
AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider(
conStrOpts
);
var tokenCredentials = new TokenCredentials(
await azureServiceTokenProvider
.GetAccessTokenAsync("https://management.azure.com/")
.ConfigureAwait(false)
);
ResourceGraphClient argClient = new ResourceGraphClient(tokenCredentials);

ADFS : Acquiring a Token programmatically

So we have built a set of Azure Functions that are secured by ADFS (MSAL)
We have configured an App in ADFS and got it all working well with our Android client.
We now want to do some API testing so we want to programmatically generate Auth tokens to test the API's
I can't get the following code to work at all, maybe I have the tenant ID wrong, in the App config, its a GUID (42b03d0b-d7f2-403e-b764-0dbdcf0505f6), but examples say it's our domain
string userName = "-";
string password = "-";
string clientId = "ee13c922-bf4b-4f0a-ba39-ea74e1203c6e";
var credentials = new UserPasswordCredential(userName, password);
var authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/acostaonline.onmicrosoft.com");
var result = await authenticationContext.AcquireTokenAsync("https://graph.windows.net", clientId, credentials);
UPDATE
So changed the code to be MSAL and still trying to login via username and password. Now it just times out
string authority = "https://login.microsoftonline.com/42b03d0b-d7f2-403e-b764-0dbdcf0505f6/";
string[] scopes = new string[] { "user.read" };
PublicClientApplication app = new PublicClientApplication("ee13c922-bf4b-4f0a-ba39-ea74e1203c6e", authority);
var accounts = await app.GetAccountsAsync();
Microsoft.Identity.Client.AuthenticationResult result = null;
if (accounts.Any())
{
result = await app.AcquireTokenSilentAsync(scopes, accounts.FirstOrDefault());
}
else
{
try
{
var securePassword = new SecureString();
foreach (char c in "PASSWORD") // you should fetch the password keystroke
securePassword.AppendChar(c); // by keystroke
result = await app.AcquireTokenByUsernamePasswordAsync(scopes, "AUSER#acosta.com",
securePassword);
}
}
Error
SocketException: A connection attempt failed because the connected
party did not properly respond after a period of time, or established
connection failed because connected host has failed to respond
172.26.200.77:443
It seems that the code you provided is using ADAL instead of MSAL.
The main difference is that with ADAL you would use an AuthenticationContext to acquire tokens, whereas in MSAL you use ConfidentialClientApplication or PublicClientApplication, depending on if the application is running in a back-end or on the user's device.
Here is the article about Differences between ADAL.NET and MSAL.NET applications.
When you use MSAL.Net to get a token for the Microsoft Graph API, you could use the following code:
public static PublicClientApplication PublicClientApp = new
PublicClientApplication(ClientId);
var app = App.PublicClientApp;
ResultText.Text = string.Empty;
TokenInfoText.Text = string.Empty;
var accounts = await app.GetAccountsAsync();
authResult = await app.AcquireTokenSilentAsync(_scopes, accounts.FirstOrDefault());
For more details, you could refer to this article, in left menu also includes Android and iOS.

azure acive directory validation error

Im having an issue with a web app in azure. I use active directory to control access which works well, but one of my users gets this error message when they login in
idx10214 audience validation failed did not match validationparameters.validaudience or validationparameters null
does anyone know what this means ? Is there a workaround ?
heres how I setup openid to authorize users, how would i include the audience settings here ? What should the audience value be ?
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = AuthorityCHP,
PostLogoutRedirectUri = postLogoutRedirectUri,
RedirectUri = loURL,
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;
Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential credential = new Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential(clientId, appKey);
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(AuthorityCHP, new ADALTokenCache(signedInUserID));
var newuri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path));
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(code, newuri, credential, graphResourceId);
return Task.FromResult(0);
}
}
});
Ive added some additional code in the hope it might work, but after i deployed it, i got the user affecetd to try logging in again and its still the same. Im not consuming a webapi its just a straightforwrsd webapp with a standard login page, Im really stumped, why is it only one particular user thats affecetd by this ? Can anynoe help ?
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = clientId
},
Tenant = tenantId,
AuthenticationType = "OAuth2Bearer"
});
That's mean you were calling the API using the incorrect token. When we call a web API which protected by Azure AD, it will verify the signature of the token and claims in the token.
The audience is used to which resource the token is able to access. We should acquire the token based on the resource. For example, if we protect the web API with code below, we should use the audience config in the below to acquire the token.
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = ConfigurationManager.AppSettings["ida:Audience"],
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
});
//app.UsePasswordAuthentication();
}
update
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
// In a real application you would use IssuerValidator for additional checks, like making sure the user's organization has signed up for your app.
// IssuerValidator = (issuer, token, tvp) =>
// {
// //if(MyCustomTenantValidation(issuer))
// return issuer;
// //else
// // throw new SecurityTokenInvalidIssuerException("Invalid issuer");
// },
},

IdentityServer4 - is it possible to use local login form with external provider and no round trip?

I'm trying to use a local login form to authenticate a user credentials against its external provider (Azure Active Directory).
I understand that, per client, you can enable local login. That helps, as when set to true, I'll get the local login form but but I'm still unclear as to how to fire off the middle ware for that external provider. Is there a way to send client credentials to the external provider to receive an ID token? My current code redirects to the Microsoft login; and then back to my identity server, and then the client application. I want the user to login in through identity server but not have them know it's really authenticating against Azure.
Here's my start up:
var schemeName = "Azure-AD";
var dataProtectionProvibder = app.ApplicationServices.GetRequiredService<IDataProtectionProvider>();
var distributedCache = app.ApplicationServices.GetRequiredService<IDistributedCache>();
var dataProtector = dataProtectionProvider.CreateProtector(
typeof(OpenIdConnectMiddleware).FullName,
typeof(string).FullName, schemeName,
"v1");
var dataFormat = new CachedPropertiesDataFormat(distributedCache, dataProtector);
///
/// Azure AD Configuration
///
var clientId = Configuration["AzureActiveDirectory:ClientId"];
var tenantId = Configuration["AzureActiveDirectory:TenantId"];
Redirect = Configuration["AzureActiveDirectory:TenantId"];
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = schemeName,
DisplayName = "Azure-AD",
SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
ClientId = clientId,
Authority = $"https://login.microsoftonline.com/{tenantId}",
ResponseType = OpenIdConnectResponseType.IdToken,
StateDataFormat = dataFormat,
});
app.UseIdentity();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
This is the login.
[HttpGet]
public async Task<IActionResult> ExternalLogin(string provider, string returnUrl)
{
var context = this.HttpContext.Authentication;
List<AuthenticationDescription> schemes = context.GetAuthenticationSchemes().ToList();
returnUrl = Url.Action("ExternalLoginCallback", new { returnUrl = returnUrl });
// start challenge and roundtrip the return URL
var props = new AuthenticationProperties
{
RedirectUri = returnUrl,
Items = { { "scheme", provider } }
};
//await HttpContext.Authentication.ChallengeAsync(provider, props);
return new ChallengeResult(provider, props);
}
In my opinion ,we shouldn't directly pass the username/password directly from other Idp to azure AD for authentication as a security implementation .And even Azure AD supports the Resource Owner Password Credentials Grant ,it's only available in native client. I suggest you keep the normal way and don't mix them .

Skipping home realm discovery with Ws-Federation OWIN Middleware

Our Mvc/WebAPI solution currently has four trusted identity providers which we have registered in ADFS3. Each of these identity providers can be used by our users by direct links, effectively working around any home-realm-cookies that ADFS may have created (eg: www.ourportal.com/accounts/facebook or www.ourportal.com/accounts/twitter). Currently we are migrating from WIF to OWIN but will keep using WS-Federation protocol for the time being by implementing wsfederation and cookie authentication middleware. When using WIF, we did the following in order to go directly to a known identity provider:
var signInRequest = new SignInRequestMessage(stsUrl, realm) { HomeRealm = homeRealm };
return new RedirectResult(signInRequest.WriteQueryString());
This seems to have two concerning behaviors, it does not pass the WsFedOwinState parameter, and on the return back to the Relying Party, the Home.cshtml is built (with a windows principal) before the the Owin authentication middleware is fired. The Home.cshtml being fired before the Owin middleware is the most concering as this view relies on Claims that would is provided in the transformation done by the authentication pipeline, which is fired afterwards and thus our view does not work. It works in the correct order when going to the portal in the normal way (eg www.ourportal.com)
I understand that in order to provide the Whr parameter, you do the following when configuring the ws-federation middleware:
RedirectToIdentityProvider = (context) =>
{
context.ProtocolMessage.Whr = "SomeUrnOfAnIdentityProvider";
return Task.FromResult(0);
}
but this sets a single identity provider for the whole solution and does not allow our users to go directly to one of a list of identity providers.
The non-working method which builds the sign-in-request is currently:
private RedirectResult FederatedSignInWithHomeRealm(string homeRealm)
{
var stsUrl = new Uri(ConfigurationManager.AppSettings["ida:Issuer"]);
string realm = ConfigurationManager.AppSettings["ida:Audience"];
var signInRequest = new SignInRequestMessage(stsUrl, realm)
{
HomeRealm = homeRealm
};
HttpContext.Request.GetOwinContext().Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType);
return new RedirectResult(signInRequest.WriteQueryString());
}
The ws-federation and cookie middleware are configured as the first middleware in OWIN startup and the default authentication is set to
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
I think I found a solution. The new method for skipping the home realm screen would be like this :
private void FederatedSignInWithHomeRealm(string homeRealm)
{
HttpContext.Request
.GetOwinContext()
.Authentication
.SignOut(CookieAuthenticationDefaults.AuthenticationType);
var authenticationProperties = new AuthenticationProperties { RedirectUri = "/" };
authenticationProperties.Dictionary.Add("DirectlyToIdentityProvider", homeRealm);
HttpContext.GetOwinContext().Authentication.Challenge(authenticationProperties);
}
And the OWIN WS-Federation middleware would be configured like this :
app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions
{
Notifications = new WsFederationAuthenticationNotifications()
{
RedirectToIdentityProvider = notification =>
{
string homeRealmId = null;
var authenticationResponseChallenge = notification.OwinContext
.Authentication
.AuthenticationResponseChallenge;
var setIdentityProvider = authenticationResponseChallenge != null
&& authenticationResponseChallenge.Properties
.Dictionary
.TryGetValue("DirectlyToIdentityProvider", out homeRealmId);
if (setIdentityProvider)
{
notification.ProtocolMessage.Whr = homeRealmId;
}
return Task.FromResult(0);
}
},
MetadataAddress = wsFedMetadata,
Wtrealm = realm,
SignInAsAuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = realm
}
});

Resources