Connecting IdentityServer4 with Azure Active Directory - azure-active-directory

As one of my requirements, I am supposed to connect the IdentitySever with an Active Directory with existing users and claims. So far I managed to create an App Registration in the Azure Portal. So I have an Appication ID and also configured an API Key. Further, I have a list of Endpoints:
https://login.windows.net/{ad_guid}/federationmetadata/2007-06/federationmetadata.xml
https://login.windows.net/{ad_guid}/wsfed
https://login.windows.net/{ad_guid}/saml2
https://login.windows.net/{ad_guid}/saml2
https://graph.windows.net/{ad_guid}
https://login.windows.net/{ad_guid}/oauth2/token
https://login.windows.net/{ad_guid}/oauth2/authorize
I can get the OpenID configuration with
https://login.windows.net/{ad_guid}/.well-known/openid-configuration
According to the documentation from Microsoft I should now configure the endpoint like this:
app.SetDefaultSignInAsAuthenticationType(
CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
var uri = "https://login.windows.net/{0}";
var instance = configuration["AzureAD:Instance"];
var authority = string.Format(CultureInfo.InvariantCulture, uri, instance);
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
DisplayName = "Azure Active Directory",
AuthenticationScheme = "AzureAD",
SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
ClientId = configuration["AzureAD:AppId"],
Authority = authority,
Scope = {"openid", "email"}
});
For some reason this is not working. Any ideas what I might have missed?

apparently, I had it almost right. here is my solution:
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme =
IdentityServerConstants.DefaultCookieAuthenticationScheme,
AutomaticAuthenticate = true,
AutomaticChallenge = true
});
public static OpenIdConnectOptions CreateAzureAdOptions(X509Certificate2 certificate2, IConfiguration configuration)
{
return new OpenIdConnectOptions
{
DisplayName = "Azure Active Directory",
AuthenticationScheme = "Azure",
ClientId = configuration["OpenId:AzureAD:AppId"],
Authority = string.Format(CultureInfo.InvariantCulture, "https://login.windows.net/{0}", configuration["OpenId:AzureAD:Instance"]),
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false
},
// https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-token-and-claims
Scope = {"openid", "email", "roles", "groups"},
Events = new OpenIdConnectEvents
{
OnRemoteFailure = context => HandleRemoteFailure(context)
},
SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme
};
}
private static Task HandleRemoteFailure(FailureContext context)
{
Log.Error(context.Failure, "Azure AD Remote Failure");
context.Response.Redirect("/accessdenied");
context.HandleResponse();
return Task.FromResult(0);
}

Related

Identity server 4 Obtain role from token in webApi app

I have correctly configured identity server 4 which authorizes a web api for method access. However, I cannot use the roles in the web api, the role is in the token but when it arrives on the web api it does not give me authorization to enter the api.
IDS4 Configuration
new Client
{
ClientId = "spaclient",
ClientName = "SPA Client",
RequireConsent = false,
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
RequirePkce = true,
RequireClientSecret = false,
AllowAccessTokensViaBrowser = true,
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"role"
}
}
public static IEnumerable<ApiScope> ApiScopes =>
new ApiScope[]
{
new ApiScope("spaclient", "SPA")
};
public static IEnumerable<ApiResource> ApiResources =>
new ApiResource[]
{
new ApiResource("spaclient", "SPA")
};
public static IEnumerable<IdentityResource> IdentityResources =>
new IdentityResource[]
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResource("role","User Role", new List<string>() { "role" })
};
CLIENT CONFIG
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://localhost:9002"; // --> IdentityServer Project
options.RequireHttpsMetadata = true;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
NameClaimType = "role",
RoleClaimType = "role"
};
});
CONTROLLER PART
[HttpGet]
[Authorize(Roles ="Administrator")] // <-- with role not work
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
[HttpGet]
[Authorize]<-- without role work fine
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
In your access token, there is no role claim. You need to configure your existing ApiScope or ApiResource to include the necessar role claim.
What you have done is to only include it in your ID-token.
see my answer here about the relationship between the various resource types in IdentityServer
To add a userclaim to your APIScope, like this:
new ApiScope(name: "spaclient",
displayName:"SPA",
userClaims: new List<string>{ "role" }),
Also, you must request the spaclient and openid scopes as well.
To control the token lifetimes:
var client2 = new Client
{
ClientId = "authcodeflowclient",
IdentityTokenLifetime = 300, //5 minutes
AccessTokenLifetime = 3600, //1 hour
AuthorizationCodeLifetime = 300, //5 minutes
AbsoluteRefreshTokenLifetime = 2592000, //30 days
SlidingRefreshTokenLifetime = 1296000, //15 days
...
To complement this answer, I write a blog post that goes into more detail about this topic:
IdentityServer – IdentityResource vs. ApiResource vs. ApiScope

How to validate Identityserver4 token validation in webapi?

Have you tried to ID4 connect with web api (.net framework 4.6) I follow below the tutorial but APIResource with secret key is not working. it also not giving any error if I give wrong API resource name and secret.
https://nahidfa.com/posts/identityserver4-and-asp-.net-web-api/
Source code
var IDSBearerOption = new IdentityServerBearerTokenAuthenticationOptions
{
AuthenticationType = "Bearer",
Authority = "https://localhost:5000",
ValidationMode = ValidationMode.Local,
RequiredScopes = new[] { "api1" },
PreserveAccessToken = true,
RoleClaimType = "role",
ValidAudiences = new[] { "TestAPI1" } ,
ClientId = "TestAPI1", //api resource name
ClientSecret = "secret1" //api resource secret
};
app.UseIdentityServerBearerTokenAuthentication(IDSBearerOption);
Is it possible to validate the token in webapi .net framework4.6?
You'll have to share cookies and set your bearer token auth options inside OWIN in startup:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
CookieHttpOnly = false,
CookieSecure = CookieSecureOption.Always,
CookieName = "MySharedCookieName"
});
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = authorityUrl,
RequiredScopes = new[] {"MyAPIName"},
ClientId = "MyAPIName",
ClientSecret = "secret",
AuthenticationType = "Bearer",
NameClaimType = "name",
RoleClaimType = "role",
SigningCertificate = x509Cert
});
Just make sure your cookie names match in IDS4 and all other apps

IdentityServer4 External Authentication (Azure Active Directory) fails

I created project from template: "is4aspid" and added Azure Active Directory authentication.
In Startup.cs added this code:
// preserve OIDC state in cache (solves problems with AAD and URL lenghts)
services.AddOidcStateDataFormatterCache();
// cookie policy to deal with temporary browser incompatibilities
services.AddSameSiteCookiePolicy();
services.AddAuthentication()
.AddOpenIdConnect("aad", "Sign-in with Azure AD", options =>
{
options.Authority = "https://login.microsoftonline.com/common";
options.ClientId = "myClientId";
options.ClientSecret = "myClientSecret";
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.SignOutScheme = IdentityServerConstants.SignoutScheme;
options.ResponseType = "id_token";
options.CallbackPath = "/signin-aad";
options.SignedOutCallbackPath = "/signout-callback-aad";
options.RemoteSignOutPath = "/signout-aad";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
NameClaimType = "name",
RoleClaimType = "role"
};
});
I added redirect uri on Azure: http://localhost:5000/signin-aad
Error occure where Callback method is called.
HttpContext.AuthenticateAsync(IdentityConstants.ExternalScheme) Success is always false
[HttpGet]
public async Task<IActionResult> Callback()
{
var result = await HttpContext.AuthenticateAsync(IdentityConstants.ExternalScheme);
if (result?.Succeeded != true)
{
throw new Exception("External authentication error");
}
// some code
}
Where is my error?
Solution
Must define IdentityConstants.ExternalScheme in startup and callback

Ca not get Claims information while login Azure active B2B directory

I can not get ClaimsPrincipal after login in azure Ad Web API,
Below is my code added in startup.auth.cs
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Scope= OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.IdToken,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = context =>
{
context.HandleResponse();
return Task.FromResult(0);
}
},
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false
},
});
I get Access Token in
result = await authContext.AcquireTokenAsync(todoListResourceId, clientCredential);
but can not get ClaimsPrincipal. I get AuthenticationType = null, IsAuthenticated = null, Name = null.
My application use adal.js for UI side to get user information, and get user information successfully.
I got Solution for this problem.replace startup.auth.cs code with
app.UseWindowsAzureActiveDirectoryBearerAuthentication( new WindowsAzureActiveDirectoryBearerAuthenticationOptions { TokenValidationParameters = new TokenValidationParameters { SaveSigninToken = true, ValidAudience = ConfigurationManager.AppSettings["ida:ClientId"], AuthenticationType = "Bearer" }, Tenant = ConfigurationManager.AppSettings["ida:Tenant"], });
this code and it working fine

Identity Server 4 - Resource Owner Password Grant and Google Authentication

I have an application that currently uses the resource owner password grant type to allow users to log in via a single page application. The identity server is hosted in the same project as the Web API currently. However, we would like to add the ability for a user to register / log in using their Google account. Currently, the user data is stored in tables and managed by ASP.NET Core Identity.
Is there a way to have both the resource owner password grant type available in the application for users who are 'local' but also enable third party authentication via Google? Currently, we hit the Identity Server token endpoint with a username and password and store the token in the browser. It's then passed to any endpoint that requires authorization. Would this same flow still work when integrating Google authentication and retrieving the Google token?
All the credit goes to Behrooz Dalvandi for this amazing post.
The solution to this problem is to create a custom grant and implement IExtensionGrantValidator.
public class GoogleGrant : IExtensionGrantValidator
{
private readonly IGoogleService _googleService;
private readonly IAccountService _accountService;
public GoogleGrant(IGoogleService googleService, IAccountService accountService)
{
_googleService = googleService;
_accountService = accountService;
}
public string GrantType
{
get
{
return "google_auth";
}
}
public async Task ValidateAsync(ExtensionGrantValidationContext context)
{
var userToken = context.Request.Raw.Get("id_token");
if (string.IsNullOrEmpty(userToken))
{
//You may want to add some claims here.
context.Result = new GrantValidationResult(TokenErrors.InvalidGrant, null);
return;
}
//Validate ID token
GoogleJsonWebSignature.Payload idTokenData = await _googleService.ParseGoogleIdToken(userToken);
if (idTokenData != null)
{
//Get user from the database.
ApplicationUser user = await _accountService.FindByEmail(idTokenData.Email);
if(user != null)
{
context.Result = new GrantValidationResult(user.Id, "google");
return;
}
else
{
return;
}
}
else
{
return;
}
}
}
Configure this validator in the Startup
var builder = services.AddIdentityServer()
.AddInMemoryIdentityResources(Config.Ids)
.AddInMemoryApiResources(Config.Apis)
.AddInMemoryClients(Config.Clients)
.AddResourceOwnerValidator<CustomResourceOwnerPasswordValidator>()
.AddExtensionGrantValidator<GoogleGrant>();//Custom validator.
And last but not the least.
Add below code in the config file. Notice the 'google_auth' grant type.
new Client
{
ClientId = "resourceownerclient",
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials.Append("google_auth").ToList(),
AccessTokenType = AccessTokenType.Jwt,
AccessTokenLifetime = 3600,
IdentityTokenLifetime = 3600,
UpdateAccessTokenClaimsOnRefresh = true,
SlidingRefreshTokenLifetime = 30,
AllowOfflineAccess = true,
RefreshTokenExpiration = TokenExpiration.Absolute,
RefreshTokenUsage = TokenUsage.OneTimeOnly,
AlwaysSendClientClaims = true,
Enabled = true,
ClientSecrets= new List<Secret> { new Secret("dataEventRecordsSecret".Sha256()) },
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.OfflineAccess,
"api1"
}
}

Resources