IdentityServer4 External Authentication (Azure Active Directory) fails - identityserver4

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

Related

IdentityServer4 fails to authenticate in memory users

We have an existing ID server configuration and I am trying to work on the UI without having to set up the rest of the connections. I followed the example to set up an MVC client:
In Startup.cs:
Console.WriteLine("Adding Test Users!!!");
serviceBulder.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddTestUsers(Config.GetUsers());
New client added:
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
RedirectUris = { "http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile
}
}
Users are configured as the following:
public static List<TestUser> GetUsers()
{
return new List<TestUser>
{
new TestUser
{
SubjectId = "1",
Username = "alice",
Password = "password"
},
new TestUser
{
SubjectId = "2",
Username = "bob",
Password = "password"
},
};
}
MVC is configured to use the services:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services
.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ClientId = "mvc";
options.ClientSecret = "secret";
options.ResponseType = "code id_token";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("openid");
options.Scope.Add("profile");
});
}
I have a couple of issues:
When I use AllowedGrantTypes = GrantTypes.Implicit per tutorial I get "Sorry, there was an error : unauthorized_client"
When I use the AllowedGrantTypes = GrantTypes.Hybrid I do get the login screen but my in memory users (alice and bob with pw = password) can't log in.
Questions:
- Does AddTestUsers not work when there are additional data stores present already?
- Why does my grant type differ from the walkthrough?
I'd check on the AllowedGrantTypes at the identity server level. We have roughly the same setup and we use AllowedGrantTypes = GrantTypes.HybridAndClientCredentials This allows the MVC application to talk with identity server behind the scenes.
The test users should be present.

How to create JWT token using IdentityServer4

In my application (.Net core application) using IdentityServer4, at present creates "Reference" Token for authentication. But I would need to change the token type from "Reference" type to "JWT" token. I found couple of articles regarding that and tried as mentioned, but still I am not able to get the "JWT" token and I am getting "Reference" token only.
I followed the details mentioned in the below sites, but no luck.
IdentityServer4 requesting a JWT / Access Bearer Token using the password grant in asp.net core
https://codebrains.io/how-to-add-jwt-authentication-to-asp-net-core-api-with-identityserver-4-part-1/
https://andrewlock.net/a-look-behind-the-jwt-bearer-authentication-middleware-in-asp-net-core/
Can anyone let me know how could we change the token type from "Reference" to "JWT" token? Is there any custom code/class to be created to achieve this?
Below is the code used in my Client class.
new Client
{
ClientId = "Client1",
ClientName = "Client1",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
AllowedScopes = new List<string>
{
IdentityScope.OpenId,
IdentityScope.Profile,
ResourceScope.Customer,
ResourceScope.Info,
ResourceScope.Product,
ResourceScope.Security,
ResourceScope.Sales,
ResourceScope.Media,
ResourceScope.Nfc,
"api1"
},
AllowOfflineAccess = true,
AlwaysSendClientClaims = true,
UpdateAccessTokenClaimsOnRefresh = true,
AlwaysIncludeUserClaimsInIdToken = true,
AllowAccessTokensViaBrowser = true,
// Use reference token so mobile user (resource owner) can revoke token when log out.
// Jwt token is self contained and cannot be revoked
AccessTokenType = AccessTokenType.Jwt,
AccessTokenLifetime = CommonSettings.AccessTokenLifetime,
RefreshTokenUsage = TokenUsage.OneTimeOnly,
RefreshTokenExpiration = TokenExpiration.Sliding,
AbsoluteRefreshTokenLifetime = CommonSettings.AbsoluteRefreshTokenLifetime,
SlidingRefreshTokenLifetime = CommonSettings.SlidingRefreshTokenLifetime,
IncludeJwtId = true,
Enabled = true
},
And in my startup.cs, I have this below code.
public void ConfigureServices(IServiceCollection services)
{
var connStr = ConfigurationManager.ConnectionStrings[CommonSettings.IDSRV_CONNECTION_STRING].ConnectionString;
services.AddMvc();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
// base-address of your identityserver
options.Authority = "http://localhost:1839/";
// name of the API resource
options.Audience = "api1";
options.RequireHttpsMetadata = false;
});
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
}
);
var builder = services.AddIdentityServer(options => setupAction(options))
.AddSigningCredential(loadCert())
.AddInMemoryClients(Helpers.Clients.Get())
.AddInMemoryIdentityResources(Resources.GetIdentityResources())
.AddInMemoryApiResources(Resources.GetApiResources()).AddDeveloperSigningCredential()
.AddConfigStoreCache().AddJwtBearerClientAuthentication()
//Adds a key for validating tokens. They will be used by the internal token validator and will show up in the discovery document.
.AddValidationKey(loadCert());
builder.AddConfigStore(options =>
{
//CurrentEnvironment.IsEnvironment("Testing") ?
// this adds the config data from DB (clients, resources)
options.ConfigureDbContext = dbBuilder => { dbBuilder.UseSqlServer(connStr); };
})
.AddOperationalDataStore(options =>
{
// this adds the operational data from DB (codes, tokens, consents)
options.ConfigureDbContext = dbBuilder => { dbBuilder.UseSqlServer(connStr); };
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = CommonSettings.TokenCleanupInterval;
});
}
Kindly let me know, what change(s) to be done to get JWT token. Thanks in advance.

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

IdentityServer4 with Windows authentication and custom claims

I have a problem with windows auth and custom claims.
I have an identityServer with windows auth and User.Identity.Name show my AD name.
But I cannot understand, how should I add some properties from my storage to this user. I have now something like this:
var claims = new List<Claim> {
new Claim("devhomepage", "www.devsite.com", ClaimValueTypes.String)};
var userIdentity = new ClaimsIdentity(claims, "siteinfo");
User.AddIdentity(userIdentity);
await HttpContext.SignInAsync(User);
return RedirectToLocal(returnUrl);
and it doesn't work :-) my client will be not authorized.
here is a config for the server
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = {"http://localhost:60640/signin-oidc"},// where to redirect to after login
PostLogoutRedirectUris = {"http://localhost:60640/signout-callback-oidc"},// where to redirect to after logout
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
},
AllowOfflineAccess = true,
RequireConsent = false
and is't a client
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = "http://localhost:49245/";
options.RequireHttpsMetadata = false;
options.ClientId = "mvc";
options.ClientSecret = "secret";
options.ResponseType = "code id_token";
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
});
For SignIn I must use Microsoft.AspNetCore.Identity.SignInManager class with method SignInAsync(/*I should take hire a TUser object, that I stored in my database, and make mapping with my AD account */)
To use custom claims (will be used for all samples with custom claims in IS4):
public class ProfileService : IProfileService
{
protected UserManager<IdentityUser> _userManager;
public ProfileService(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
//>Processing
var user = await _userManager.GetUserAsync(context.Subject);
//my custom claims
var claims = new List<Claim>
{
new Claim("devhomepage", "www"),
new Claim("reporting","reps")
};
context.IssuedClaims.AddRange(claims);
}
public async Task IsActiveAsync(IsActiveContext context)
{
//>Processing
var user = await _userManager.GetUserAsync(context.Subject);
context.IsActive = (user != null);
}
}
But this service I must register only after my IdentityService
services.AddIdentityServer()
...
services.AddTransient<IProfileService, ProfileService>();
}
of ConfigureServices

Access to API resource with Implicit flow

Does identity server 4 doesn't allow implicit flow to access API Resource.
Identity Server 4 config.cs
new Client
{
ClientId = "implicit",
ClientName = "Implicit Client",
AllowAccessTokensViaBrowser = true,
RedirectUris = { "https://notused" },
PostLogoutRedirectUris = { "https://notused" },
FrontChannelLogoutUri = "http://localhost:5000/signout-idsrv", // for testing identityserver on localhost
AccessTokenLifetime = 10,
AllowedGrantTypes = GrantTypes.Implicit,
AllowedScopes = { "openid", "profile", "email", "ProxyServer", "api" }
}
Api Resouce
new ApiResource("api", "Custom"),
new ApiResource("ProxyServer", "Proxy Server")
In Mvc Client I am using this code ConfigureServices
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = "oidc";
})
.AddCookie(options =>
{
options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
options.Cookie.Name = "mvcimplicit";
})
.AddOpenIdConnect("oidc", options =>
{
options.Authority = Constants.Authority;
options.RequireHttpsMetadata = false;
options.ClientId = "implicit";
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("ProxyServer");
options.Scope.Add("profile");
options.Scope.Add("email");
options.SaveTokens = true;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = JwtClaimTypes.Name,
RoleClaimType = JwtClaimTypes.Role,
};
});
When I try in browser I get "Sorry, there was an error : invalid_scope ". But if I remove options.Scope.Add("ProxyServer"); it works fine and Identity server 4 take me to login page.
OK I found the issue but posting just in case someone else face the same problem.
Response type needs to be specified explicitly otherwise it wont work.
options.ResponseType = "id_token token";
Modify the response type accordingly.

Resources