I implemented an SSO with IdentityServer4 and 3 clients. two of clients, are ASP.NET Core
and other is ASP.net Framework.
Both .Net Core clients, work well. but the problem is in .Net Framework client. after Login, when user navigate to unauthorized path and section, browser Redirect it to "/signin-oidc" (but in .Net Core clients, it doesn't happen) (also user login successfully, because it doesn't redirect client to Login page anymore and in two other projects, user logged in).
Whate is problem? These are my code:
Config in SSO project:
public class Config
{
...
public static IEnumerable<Client> GetClients()
{
return new List<Client>()
{
new Client()
{
ClientId = "SSO_Client",
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedGrantTypes = GrantTypes.Code,
RedirectUris = {
"https://localhost:44387/signin-oidc", //.Net Core Client
"https://localhost:44382/signin-oidc", //.Net Core Client
"https://localhost:44385/signin-oidc" //.Net Framework
},
PostLogoutRedirectUris = {
"https://localhost:44387/signout-callback-oidc", //.Net Core Client
"https://localhost:44382/signout-callback-oidc", //.Net Core Client
"https://localhost:44385/signout-callback-oidc" //.Net Framework Client
},
AllowedScopes = new List<string>()
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"organ",
"access-control"
},
AlwaysIncludeUserClaimsInIdToken = true,
}
};
}
}
Startup In .Net framework project:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies",
ExpireTimeSpan = TimeSpan.FromMinutes(60)
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = "https://localhost:44372", //SSO Project
RequireHttpsMetadata = false,
ClientId = "SSO_Client",
ClientSecret = "secret",
ResponseType = "code",
SaveTokens = true,
RedirectUri = "https://localhost:44385/signin-oidc",
PostLogoutRedirectUri = "https://localhost:44385/signout-callback-oidc",
Scope = "openid profile email organ",
SignInAsAuthenticationType = "Cookies",
});
}
}
I have an ASP.NET MVC application that is registered in Azure Active Directory. I am adding a new feature to the app which will require a list of all users from AAD. By putting together some code snippets I got from various MSDN docs, this is what my method to get the users looks like so far:
public async Task GetUsers()
{
string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
string clientSecret = ConfigurationManager.AppSettings["ida:ClientSecret"];
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenant)
.WithClientSecret(clientSecret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var users = await graphClient.Users.Request().GetAsync();
}
However, when I run the application, I get Original exception: AADSTS7000215: Invalid client secret is provided. I double checked to make sure that the client secret that I added to web.config matches what's shown in AAD, and that the client secret hasn't expired. I even deleted the client secret and created a new one, but that didn't fix the problem either. I also ensured that the permission User.Read.All of type Application has been granted for this app. What could be causing this error, and what could be done to resolve it? Also, I'm wondering if there's a simpler way to get a list of users given that I already have authentication set up for this app using Owin.IAppBuilder. Here's what I have in my Startup.cs file:
public class Startup
{
private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
private static string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
string authority = string.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions()
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = ctx => {
ctx.HandleResponse();
ctx.Response.Redirect("/Error/messages" + ctx.Exception.Message);
return Task.FromResult(0);
}
} });
}
}
It works for me this way (using WithAuthority), I did not need any additional scopes:
string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
string tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
string clientSecret = ConfigurationManager.AppSettings["ida:ClientSecret"];
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithAuthority(new Uri($"{aadInstance}{tenantId}/"))
.WithClientSecret(clientSecret)
.Build();
and then...
return await graphClient.Users
.Request()
.GetAsync();
I'm trying to create an OWIN startup class for Azure AD authentication using a sample project from Microsoft as reference. The sample is in C# but the project I'm adapting it to is in VB.NET so I need to convert the following C# to VB.NET:
public class Startup
{
// The Client ID is used by the application to uniquely identify itself to Azure AD.
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 Azure Active Directory v2 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);
/// <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 id_token - which contains basic information about the signed-in user
ResponseType = OpenIdConnectResponseType.IdToken,
// ValidateIssuer set to false to allow personal and work accounts from any organization to sign in to your application
// To only allow users from a single organizations, set ValidateIssuer to true and 'tenant' setting in web.config to the tenant name
// To allow users from only a list of specific organizations, set ValidateIssuer to true and use ValidIssuers parameter
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false
},
// OpenIdConnectAuthenticationNotifications configures OWIN to send notification of failed authentications to OnAuthenticationFailed method
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed
}
}
);
}
/// <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("/?errormessage=" + context.Exception.Message);
return Task.FromResult(0);
}
}
Here's my converted VB.NET:
Public Class Startup
Private clientId As String = ConfigurationManager.AppSettings("ClientId")
Private redirectUri As String = ConfigurationManager.AppSettings("RedirectUri")
Shared tenant As String = ConfigurationManager.AppSettings("Tenant")
Private authority As String = String.Format(Globalization.CultureInfo.InvariantCulture, ConfigurationManager.AppSettings("Authority"), tenant)
Public Sub Configuration(ByVal app As IAppBuilder)
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType)
app.UseCookieAuthentication(New CookieAuthenticationOptions())
app.UseOpenIdConnectAuthentication(New OpenIdConnectAuthenticationOptions With {
.ClientId = clientId,
.Authority = authority,
.RedirectUri = redirectUri,
.PostLogoutRedirectUri = redirectUri,
.Scope = OpenIdConnectScope.OpenIdProfile,
.ResponseType = OpenIdConnectResponseType.IdToken,
.TokenValidationParameters = New TokenValidationParameters() With {
.ValidateIssuer = False
},
.Notifications = New OpenIdConnectAuthenticationNotifications With {
.AuthenticationFailed = OnAuthenticationFailed() '<---ERROR HERE
}
})
End Sub
Private Function OnAuthenticationFailed(ByVal context As AuthenticationFailedNotification(Of OpenIdConnectMessage, OpenIdConnectAuthenticationOptions)) As Task
context.HandleResponse()
context.Response.Redirect("/?errormessage=" & context.Exception.Message)
Return Task.FromResult(0)
End Function
End Class
I'm getting an error, though, on the line .AuthenticationFailed = OnAuthenticationFailed(). The error says I need to pass in the context parameter which makes sense but I'm confused about why it wasn't required in the C# version and what I'm supposed to pass in the VB.NET.
Thanks!
I ended up just writing the OnAuthenticationFailed() function inline:
Public Sub Configuration(ByVal app As IAppBuilder)
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType)
app.UseCookieAuthentication(New CookieAuthenticationOptions())
app.UseOpenIdConnectAuthentication(New OpenIdConnectAuthenticationOptions With {
.ClientId = clientId,
.Authority = authority,
.RedirectUri = redirectUri,
.PostLogoutRedirectUri = redirectUri,
.Scope = OpenIdConnectScope.OpenIdProfile,
.ResponseType = OpenIdConnectResponseType.IdToken,
.TokenValidationParameters = New TokenValidationParameters() With {
.ValidateIssuer = True
},
.Notifications = New OpenIdConnectAuthenticationNotifications With {
.AuthenticationFailed = Function(context)
context.HandleResponse()
context.Response.Redirect(HttpContext.Current.Server.MapPath("~") & "?errormessage=" & context.Exception.Message)
Return Task.FromResult(0)
End Function
}
})
End Sub
I am using Identity Server 4 and Implicit Flow and want to add some claims to the access token, the new claims or attributes are "tenantId" and "langId".
I have added langId as one of my scopes as below and then requesting that through identity server, but i get the tenantId also. How can this happen?
This the list of scopes and client configuration:
public IEnumerable<Scope> GetScopes()
{
return new List<Scope>
{
// standard OpenID Connect scopes
StandardScopes.OpenId,
StandardScopes.ProfileAlwaysInclude,
StandardScopes.EmailAlwaysInclude,
new Scope
{
Name="langId",
Description = "Language",
Type= ScopeType.Resource,
Claims = new List<ScopeClaim>()
{
new ScopeClaim("langId", true)
}
},
new Scope
{
Name = "resourceAPIs",
Description = "Resource APIs",
Type= ScopeType.Resource
},
new Scope
{
Name = "security_api",
Description = "Security APIs",
Type= ScopeType.Resource
},
};
}
Client:
return new List<Client>
{
new Client
{
ClientName = "angular2client",
ClientId = "angular2client",
AccessTokenType = AccessTokenType.Jwt,
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris = new List<string>(redirectUris.Split(',')),
PostLogoutRedirectUris = new List<string>(postLogoutRedirectUris.Split(',')),
AllowedCorsOrigins = new List<string>(allowedCorsOrigins.Split(',')),
AllowedScopes = new List<string>
{
"openid",
"resourceAPIs",
"security_api",
"role",
"langId"
}
}
};
I have added the claims in the ProfileService:
public class ProfileService : IdentityServer4.Services.IProfileService
{
private readonly SecurityCore.ServiceContracts.IUserService _userService;
public ProfileService(SecurityCore.ServiceContracts.IUserService userService)
{
_userService = userService;
}
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
//hardcoded them just for testing purposes
List<Claim> claims = new List<Claim>() { new Claim("langId", "en"), new Claim("tenantId", "123") };
context.IssuedClaims = claims;
return Task.FromResult(0);
}
This is what i am requesting to get the token, the problem is i am only requesting the langId but I am getting both the tenantId and langId in the access token
http://localhost:44312/account/login?returnUrl=%2Fconnect%2Fauthorize%2Flogin%3Fresponse_type%3Did_token%2520token%26client_id%3Dangular2client%26redirect_uri%3Dhttp%253A%252F%252Flocalhost:5002%26scope%3DresourceAPIs%2520notifications_api%2520security_api%2520langId%2520navigation_api%2520openid%26nonce%3DN0.73617935552798141482424408851%26state%3D14824244088510.41368537145696305%26
Decoded access token:
{
"nbf": 1483043742,
"exp": 1483047342,
"iss": "http://localhost:44312",
"aud": "http://localhost:44312/resources",
"client_id": "angular2client",
"sub": "1",
"auth_time": 1483043588,
"idp": "local",
"langId": "en",
"tenantId": "123",
"scope": [
"resourceAPIs",
"security_api",
"langId",
"openid"
],
"amr": [
"pwd"
]
}
This answer was written for Identityserver4 on .Net core 2 to use it for .Net core 3, this answer may help you, but you need to test and change a few things.
I am using asp.net Identity and Entity Framework with Identityserver4.
This is my sample code, works well and JWT contains all roles and claims
You can see how to implement Identityserver4 with ASP.Net core identity here
http://docs.identityserver.io/en/release/quickstarts/6_aspnet_identity.html
https://github.com/IdentityServer/IdentityServer4.Samples/tree/dev/Quickstarts/6_AspNetIdentity
1- identity server startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddMvc();
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
//Add IdentityServer services
//var certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "LocalhostCert.pfx"), "123456");
services.AddIdentityServer()
.AddTemporarySigningCredential()
.AddInMemoryIdentityResources(Configs.IdentityServerConfig.GetIdentityResources())
.AddInMemoryApiResources(Configs.IdentityServerConfig.GetApiResources())
.AddInMemoryClients(Configs.IdentityServerConfig.GetClients())
.AddAspNetIdentity<ApplicationUser>()
.AddProfileService<Configs.IdentityProfileService>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
//app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseIdentity();
// Adds IdentityServer
app.UseIdentityServer();
// Add external authentication middleware below. To configure them please see https://go.microsoft.com/fwlink/?LinkID=532715
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Account}/{action=Login}/{id?}");
});
}
2- IdentityServerConfig.cs
using IdentityServer4;
using IdentityServer4.Models;
using System.Collections.Generic;
namespace IdentityAuthority.Configs
{
public class IdentityServerConfig
{
// scopes define the resources in your system
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile()
};
}
// scopes define the API resources
public static IEnumerable<ApiResource> GetApiResources()
{
//Create api resource list
List<ApiResource> apiResources = new List<ApiResource>();
//Add Application Api API resource
ApiResource applicationApi = new ApiResource("ApplicationApi", "Application Api");
applicationApi.Description = "Application Api resource.";
apiResources.Add(applicationApi);
//Add Application Api API resource
ApiResource definitionApi = new ApiResource("DefinitionApi", "Definition Api");
definitionApi.Description = "Definition Api.";
apiResources.Add(definitionApi);
//Add FF API resource
ApiResource ffApi = new ApiResource("FFAPI", "Fule .netfx API");
ffApi.Description = "Test using .net 4.5 API application with IdentityServer3.AccessTokenValidation";
apiResources.Add(ffApi);
return apiResources;
}
// client want to access resources (aka scopes)
public static IEnumerable<Client> GetClients()
{
//Create clients list like webui, console applications and...
List<Client> clients = new List<Client>();
//Add WebUI client
Client webUi = new Client();
webUi.ClientId = "U2EQlBHfcbuxUo";
webUi.ClientSecrets.Add(new Secret("TbXuRy7SSF5wzH".Sha256()));
webUi.ClientName = "WebUI";
webUi.AllowedGrantTypes = GrantTypes.HybridAndClientCredentials;
webUi.RequireConsent = false;
webUi.AllowOfflineAccess = true;
webUi.AlwaysSendClientClaims = true;
webUi.AlwaysIncludeUserClaimsInIdToken = true;
webUi.AllowedScopes.Add(IdentityServerConstants.StandardScopes.OpenId);
webUi.AllowedScopes.Add(IdentityServerConstants.StandardScopes.Profile);
webUi.AllowedScopes.Add("ApplicationApi");
webUi.AllowedScopes.Add("DefinitionApi");
webUi.AllowedScopes.Add("FFAPI");
webUi.ClientUri = "http://localhost:5003";
webUi.RedirectUris.Add("http://localhost:5003/signin-oidc");
webUi.PostLogoutRedirectUris.Add("http://localhost:5003/signout-callback-oidc");
clients.Add(webUi);
//Add IIS test client
Client iisClient = new Client();
iisClient.ClientId = "b8zIsVfAl5hqZ3";
iisClient.ClientSecrets.Add(new Secret("J0MchGJC8RzY7J".Sha256()));
iisClient.ClientName = "IisClient";
iisClient.AllowedGrantTypes = GrantTypes.HybridAndClientCredentials;
iisClient.RequireConsent = false;
iisClient.AllowOfflineAccess = true;
iisClient.AlwaysSendClientClaims = true;
iisClient.AlwaysIncludeUserClaimsInIdToken = true;
iisClient.AllowedScopes.Add(IdentityServerConstants.StandardScopes.OpenId);
iisClient.AllowedScopes.Add(IdentityServerConstants.StandardScopes.Profile);
iisClient.AllowedScopes.Add("ApplicationApi");
iisClient.AllowedScopes.Add("DefinitionApi");
iisClient.AllowedScopes.Add("FFAPI");
iisClient.ClientUri = "http://localhost:8080";
iisClient.RedirectUris.Add("http://localhost:8080/signin-oidc");
iisClient.PostLogoutRedirectUris.Add("http://localhost:8080/signout-callback-oidc");
clients.Add(iisClient);
return clients;
}
}
}
3 - IdentityProfileService.cs
using IdentityServer4.Services;
using System;
using System.Threading.Tasks;
using IdentityServer4.Models;
using IdentityAuthority.Models;
using Microsoft.AspNetCore.Identity;
using IdentityServer4.Extensions;
using System.Linq;
namespace IdentityAuthority.Configs
{
public class IdentityProfileService : IProfileService
{
private readonly IUserClaimsPrincipalFactory<ApplicationUser> _claimsFactory;
private readonly UserManager<ApplicationUser> _userManager;
public IdentityProfileService(IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory, UserManager<ApplicationUser> userManager)
{
_claimsFactory = claimsFactory;
_userManager = userManager;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var sub = context.Subject.GetSubjectId();
var user = await _userManager.FindByIdAsync(sub);
if (user == null)
{
throw new ArgumentException("");
}
var principal = await _claimsFactory.CreateAsync(user);
var claims = principal.Claims.ToList();
//Add more claims like this
//claims.Add(new System.Security.Claims.Claim("MyProfileID", user.Id));
context.IssuedClaims = claims;
}
public async Task IsActiveAsync(IsActiveContext context)
{
var sub = context.Subject.GetSubjectId();
var user = await _userManager.FindByIdAsync(sub);
context.IsActive = user != null;
}
}
}
4 - In my client mvc core project I added 3 nuget packages
.Microsoft.AspNetCore.Authentication.Cookies
.Microsoft.AspNetCore.Authentication.OpenIdConnect
.IdentityModel
5- This is my startup.cs in my client mvc core project
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
//app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
//Setup OpenId and Identity server
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies",
AutomaticAuthenticate = true
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
Authority = "http://localhost:5000",
ClientId = "U2EQlBHfcbuxUo",
ClientSecret = "TbXuRy7SSF5wzH",
AuthenticationScheme = "oidc",
SignInScheme = "Cookies",
SaveTokens = true,
RequireHttpsMetadata = false,
GetClaimsFromUserInfoEndpoint = true,
ResponseType = "code id_token",
Scope = { "ApplicationApi", "DefinitionApi", "FFAPI", "openid", "profile", "offline_access" },
TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
}
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
6 - In my API I added this nuget package
.IdentityServer4.AccessTokenValidatio
and my startup.cs is like this
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
//IdentityServer4.AccessTokenValidation
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:5000",
RequireHttpsMetadata = false,
ApiName = "ApplicationApi"
});
app.UseMvc();
}
Now I can use [Authorize(Role="SuperAdmin, Admin")] in both client web app and API app.
User.IsInRole("Admin")
also I have access to claims
HttpContext.User.Claims
var q = (from p in HttpContext.User.Claims where p.Type == "role" select p.Value).ToList();
var q2 = (from p in HttpContext.User.Claims where p.Type == "sub" select p.Value).First();
I would like to provide my own answer after some rigorous research:
During the login process, the server will issue an authentication cookie with some of the claims of the user.
Then, the client will request an access token while providing the claims from the cookie, and the profile service will use the cookie claims to generate the access token claims.
Next, the client will request an id token, but this time it will use the claims from the access token.
Now the thing is, the default profile service of identity server populates the claims of the id token just by using the claims in the access token, while the default profile service of ASP.Net Identity, does look up all the user claims from the database store. This is a point of confusion.
For the identity server implementation, which claims end up in the access token? The claims associated with scopes which are API resources, as opposed to the claims in the id token, which are those associated with scopes which are identity resources.
Summary
Without ASP.NET Identity:
Login - identity server issues a cookie with some claims
Access token query - identity server adds claims from the cookie based on requested api scopes
Id token query - identity server adds claims from the access token based on requested identity scopes
With ASP.NET Identity:
Login - identity server issues a cookie with some claims
Access token query - identity server adds claims from the cookie based on requested api scopes
Id token query - identity server adds claims from the access token and the claims in the user store based on requested identity scopes
You should check context.RequestedClaimTypes and filter out claims, that were not requested.
In an Action in a Controller, we have the AuthenticationResult which contains AccessToken.
We would like to do a manual login with this token that is equal to the UserCookieAuthentication owin middleware. Is that possible?
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
},
});
}
}
I have been looking at HttpContext.GetOwinContext().Authentication.SignIn() but that doesn't take any Tokens.
Looking in the Katana/Owin Github project the following TokenHelper solved it along with using the AuthenticationResponseGrant method:
result = await authContext.AcquireTokenAsync(resource, clientId, uc);
var principal = await new TokenHelper().GetValidatedClaimsPrincipalAsync(result.AccessToken);
var claimsIdentity = new ClaimsIdentity(principal.Claims, CookieAuthenticationDefaults.AuthenticationType);
var properties = new AuthenticationProperties();
HttpContext.GetOwinContext().Authentication.AuthenticationResponseGrant =
new AuthenticationResponseGrant(claimsIdentity, properties);