Identityserver3 error Unable to get document from: https://localhost:44300/identity/.well-known/openid-configuration - asp.net-web-api

I have created an identityserver to issue token. Using identityserver3 to setup.
I am using a local .pfx certificate with password for signing the tokens.
this was working good but not sure why i am getting the following error as shown in the attachment.
Really making me crazy.
Below is the code in startup.cs on the authorization server. The certificate file is located in \bin\debug folder
public class X509Certificate2Wrapper : IX509Certificate2Wrapper
{
public X509Certificate2 LoadCertificate(string filename, string password)
{
var path = $#"{AppDomain.CurrentDomain.BaseDirectory}{filename}";
return new X509Certificate2(path, password);
}
}
app.Map("/identity", idsrvApp =>
{
idsrvApp.UseIdentityServer(new IdentityServerOptions
{
SiteName = "Identity Manager",
IssuerUri = Common.Constants.IdSrvIssuerUri,
SigningCertificate = X509Certificate2Wrapper.LoadCertificate(CertificateFilename, CertificatePassword),
Factory = factory,
RequireSsl = true,
EnableWelcomePage = false,
AuthenticationOptions = new AuthenticationOptions
{
EnableSignOutPrompt = false,
EnablePostSignOutAutoRedirect = true,
PostSignOutAutoRedirectDelay = 3,
CookieOptions = new IdentityServer3.Core.Configuration.CookieOptions
{
ExpireTimeSpan = new TimeSpan(0, IdentityServerServices.AuthenticationTimeout(), 0),
SlidingExpiration = true
},
//
// Note: Uncomment following line to enable WindowsAuthentication only - logout related settings will also require removal!
//
//EnableLocalLogin = false,
IdentityProviders = ConfigureIdentityProviders
},
Endpoints = new EndpointOptions
{
EnableAccessTokenValidationEndpoint = true,
EnableAuthorizeEndpoint = true,
EnableCheckSessionEndpoint = false,
EnableClientPermissionsEndpoint = false,
EnableCspReportEndpoint = false,
EnableDiscoveryEndpoint = true,
EnableEndSessionEndpoint = true,
EnableIdentityTokenValidationEndpoint = true,
EnableIntrospectionEndpoint = false,
EnableTokenEndpoint = true,
EnableTokenRevocationEndpoint = false,
EnableUserInfoEndpoint = true
}
});
});

Not sure this is the perfect answer. But i did the following and all working fine for me.
i) followed steps regarding certificatesas mentioned in https://github.com/IdentityServer/IdentityServer3.Samples/tree/master/source/Certificates
ii) To resolve this I moved the "localhost" IIS Express Cert from the Personal CertStore to the Trusted Root Certification Authorities and the issue was gone.

Related

Error coming string reference not set to an instance of a String. Parameter name: s

I am working on authentication and authorization in JWT. But I have an error coming which do not not why.
builder.Services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(builder.Configuration.GetSection("Jwt:Key").Value)),
ValidateIssuer = false,
ValidateAudience = false
};
});
I add code like below in appsettings.json :
"JWT": {
"Key": "fc746b61cde4f6665d3f9791446cd5395661860c0075a905ed9810b7391af467",
"Issuer": "Comply",
"Audience": "comply"
}
In Program.cs:
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JWT:Key"]))

ASP.NET Web API work cors with identity server 4

I try to connect the ASP.NET Web API (not .NET Core) with identity server.
I use owin as startup but I get a cors error.
Why does cors
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
not work with UseOpenIdConnectAuthentication? It works when I remove it
public class Startup1
{
public void Configuration(IAppBuilder app)
{
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = "insig_spa",
Authority = "https://localhost:5000",
RedirectUri = "https://localhost:5002/auth-callback",
Scope = "openid profile email insigapi.read",
SignInAsAuthenticationType = "cookie",
RequireHttpsMetadata = false,
UseTokenLifetime = false,
RedeemCode = true,
SaveTokens = true,
ClientSecret = "secret",
ResponseType = OpenIdConnectResponseType.Code,
ResponseMode = "query",
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication)
{
// set PKCE parameters
var codeVerifier = CryptoRandom.CreateUniqueId(32);
string codeChallenge;
using (var sha256 = SHA256.Create())
{
var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
codeChallenge = Base64Url.Encode(challengeBytes);
}
n.ProtocolMessage.SetParameter("code_challenge", codeChallenge);
n.ProtocolMessage.SetParameter("code_challenge_method", "S256");
// remember code_verifier (adapted from OWIN nonce cookie)
RememberCodeVerifier(n, codeVerifier);
}
return Task.CompletedTask;
},
AuthorizationCodeReceived = n =>
{
// get code_verifier
var codeVerifier = RetrieveCodeVerifier(n);
// attach code_verifier
n.TokenEndpointRequest.SetParameter("code_verifier", codeVerifier);
return Task.CompletedTask;
}
}
});
}
}

invalid_grant error IdentityServer 3 & asp.net core

I have an ASP.NET Core 2 MVC app using identity server 3 with Hybrid flow with an intention of fetching access tokens also which i can use further for accessing API's, sometimes I am redirected to the IDP login page and after entering username and password i am redirected back to the MVC app, but it is failing randomly.
I have the following configuration
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddOpenIdConnect(options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = authConfig.GetValue<string>("Authority");
options.RequireHttpsMetadata = false;
options.ClientId = authConfig.GetValue<string>("ClientId");
options.ClientSecret = authConfig.GetValue<string>("ClientSecret");
options.ResponseType = "code id_token token";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = false;
options.TokenValidationParameters = new
TokenValidationParameters
{
NameClaimType = ClaimTypes.Name,
RoleClaimType = ClaimTypes.Role
};
options.Events = new OpenIdConnectEvents
{
OnRemoteFailure = context =>
{
context.HttpContext.Response.Redirect($"/Error?RequestId=4000&errormessage={context.Failure?.Message }");
context.HandleResponse();
return Task.FromResult(0);
},
OnRedirectToIdentityProvider = context =>
{
//TODO: Get IdentityProvider value for Multiple subscribers and not from config
var idp = authConfig.GetValue<string>("IdentityProvider");
var acrValues = new List<string>();
if (!string.IsNullOrWhiteSpace(idp))
acrValues.Add($"idp:{idp}");
if (acrValues.Count > 0)
context.ProtocolMessage.AcrValues = string.Join(" ", acrValues);
//if (context.ProtocolMessage.RequestType != OpenIdConnectRequestType.Logout)
//{
// if (!CurrentEnvironment.IsDevelopment() &&
// context.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication)
// {
// // in widget iframe skip prompt login screen
// context.ProtocolMessage.Prompt = "none";
// }
// return Task.FromResult(0);
//}
var idTokenHint = context.HttpContext.User.FindFirst("id_token");
if (idTokenHint != null)
context.ProtocolMessage.IdTokenHint = idTokenHint.Value;
return Task.FromResult(0);
}
and the configuration on Identity server for the client is like
"ClientName": "SampleApp",
"ClientId": "sample.app.mvc",
"Flow": 2,
"RedirectUris": ["https://localhost:44368/signin-oidc"],
"PostLogoutRedirectUris": ["https://localhost:44368/"],
"PrefixClientClaims": true,
"RequireConsent": false,
"AllowedScopes":
[
"openid",
"profile",
"roles",
"CustomScope"
],
"Claims": [{
"Type": "subscriberId",
"Value": "dcbe85c6-05b6-470d-b558-289d1ae3bb15"
}],
"ClientSecrets": [{
"Secret": "tudc73K2y7pnEjT2"
}],
"IdentityTokenLifetime": 300,
"AccessTokenLifetime": 3600,
"AuthorizationCodeLifetime": 300,
"EnableLocalLogin": true
}
I keep hitting the error invalid_grant most of the times when i try in browsers. Can you please tell me what part of the configuration is incorrect?
I have found what the issue is here.
It wasn't happening because of any configuration mentioned above. But because of the fact that I was using InMemory store for AuthorizationCode store, and also my identity server was deployed on Azure and had 2 instances.

IdentityServer4 Net Core 2 not calling custom iProfileService

I've upgraded my Identity Server project to Net Core 2 and now I am not able to get the iProfileService object to be called to add in custom user claims. It did work in Net Core 1.
Startup.cs ConfigureServices function
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
services.AddTransient<IProfileService, M25ProfileService>();
//Load certificate
var cert = new X509Certificate2(Path.Combine(_environment.ContentRootPath,
"m25id-cert.pfx"), "mypassword");
services.AddIdentityServer()
.AddSigningCredential(cert)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
//options.EnableTokenCleanup = true;
//options.TokenCleanupInterval = 30;
})
.AddProfileService<M25ProfileService>();
.AddAspNetIdentity<ApplicationUser>();
M25ProfileService.cs
public class M25ProfileService : IProfileService
{
public M25ProfileService(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var user = _userManager.GetUserAsync(context.Subject).Result;
var claims = new List<Claim>
{
new Claim(JwtClaimTypes.GivenName, user.FirstName),
new Claim(JwtClaimTypes.FamilyName, user.LastName),
new Claim(IdentityServerConstants.StandardScopes.Email, user.Email),
new Claim("uid", user.Id),
new Claim(JwtClaimTypes.ZoneInfo, user.TimeZone)
};
if (user.UserType != null)
claims.Add(new Claim("mut", ((int)user.UserType).ToString()));
context.IssuedClaims.AddRange(claims);
return Task.FromResult(0);
}
public Task IsActiveAsync(IsActiveContext context)
{
var user = _userManager.GetUserAsync(context.Subject).Result;
context.IsActive = user != null;
return Task.FromResult(0);
}
}
}
Config.cs
public class Config
{
// try adding claims to id token
public static IEnumerable<IdentityResource> GetIdentityResources()
{
var m25Profile = new IdentityResource(
"m25.profile",
"m25 Profile",
new[]
{
ClaimTypes.Name,
ClaimTypes.Email,
IdentityServerConstants.StandardScopes.OpenId,
JwtClaimTypes.GivenName,
JwtClaimTypes.FamilyName,
IdentityServerConstants.StandardScopes.Email,
"uid",
JwtClaimTypes.ZoneInfo
}
);
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email(),
m25Profile
};
}
public static IEnumerable<ApiResource> GetApiResources()
{
//Try adding claims to access token
return new List<ApiResource>
{
new ApiResource(
"m25api",
"message25 API",
new[]
{
ClaimTypes.Name,
ClaimTypes.Email,
IdentityServerConstants.StandardScopes.OpenId,
JwtClaimTypes.GivenName,
JwtClaimTypes.FamilyName,
IdentityServerConstants.StandardScopes.Email,
"uid",
JwtClaimTypes.ZoneInfo
}
)
};
}
public static IEnumerable<Client> GetClients()
{
// client credentials client
return new List<Client>
{
new Client
{
ClientId = "client",
ClientName = "Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"m25api"
}
},
// Local Development Client
new Client
{
ClientId = "m25AppDev",
ClientName = "me25",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RequireConsent = false,
RedirectUris = { "http://localhost:4200/authorize.html" },
PostLogoutRedirectUris = { "http://localhost:4200/index.html" },
AllowedCorsOrigins = { "http://localhost:4200" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
JwtClaimTypes.GivenName,
"mut",
"m25api"
},
AllowOfflineAccess = true,
IdentityTokenLifetime = 300,
AccessTokenLifetime = 86400
}
};
}
}
The first thing I'm trying is just to get the identity server to allow me to login and show the user claims similar to the id4 samples. When I login, the standard claims are listed but none of the custom claims. I've put break points in the M25ProfileService class but they never get hit. It seems that ID4 is never using the customer ProfileService class but I do have it in my startup.cs.
I've also tried from my test JS Client and get the same results. Here's a snippet from my JS Client:
var config = {
authority: "http://localhost:5000",
client_id: "m25AppDev",
redirect_uri: "http://localhost:4200/authorize.html",
response_type: "id_token token",
scope:"openid profile m25api",
post_logout_redirect_uri : "http://localhost:4200/index.html"
};
var mgr = new Oidc.UserManager(config);
mgr.getUser().then(function (user) {
if (user) {
log("User logged in", user.profile);
document.getElementById("accessToken").innerHTML = "Bearer " + user.access_token + "\r\n";
}
else {
log("User not logged in");
}
});
function login() {
mgr.signinRedirect();
}
At this point, I'm not sure what to try. I thought if I added the claims to the id token (GetIdentityResources() function from what I understand) and even the access token (GetApiResources() function from what I understand), I'd see the claims but nothing seems to work. Please help! Thanks in advance!
Also, I used to be able to get the custom claims from my client as well as from the Identity Server's own index page that renders after log
Change the order of these lines of code:
.AddProfileService<M25ProfileService>()
.AddAspNetIdentity<ApplicationUser>();
One if overwriting the other.
I figured it out. Thanks to some code on GitHub, I was able to figure out what I was missing. I just needed to add these 2 lines to each client's config in config.cs and all worked perfect!
AlwaysSendClientClaims = true,
AlwaysIncludeUserClaimsInIdToken = true
This works for remote clients. However, I still can't get it to work when I'm on the ID Server itself logging in (not from a client). That's not a big deal for now but could be something in the future. If/When I figure that piece out, I'll try to remember to update my answer. Meanwhile, I hope this helps others.
In addition to the answers above (and beside the fact that the Startup.cs shown in the question already contained the relevant line of code) I'd like to add another, yet very simple cause for why the Profile Service might not be called:
Don't forget to register the service with the dependency injection container!
As having just .AddProfileService<ProfileService>() is not enough.
You would also need:
services.AddScoped<IProfileService, ProfileService>();
Or:
services.AddTransient<IProfileService, ProfileService>();

Custom statusText for JWT Token MVC Core

I have implemented JWT Token authentication in my MVC Core Application using the following articles:
Link 1
Link 2
This is what I have in my Startup.cs
private const string SecretKey = "MySecretKey"; //TODO: remove hard coded get from environments as suggested in Blog
private readonly SymmetricSecurityKey _signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SecretKey));
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
var jwtAppSettingOptions = Configuration.GetSection(nameof(JwtIssuerOptions));
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],
ValidateAudience = true,
ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)],
ValidateIssuerSigningKey = true,
IssuerSigningKey = _signingKey,
RequireExpirationTime = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = tokenValidationParameters,
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
name: "areaRoute",
template: "{area:exists}/{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
});
}
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc(options => { });
services.Configure<MvcOptions>(options => { });
services.Configure<IISOptions>(options => { });
services.AddOptions();
#region Authentication and Authorisation
services.AddAuthorization(options =>
{
using (var dbContext = new FoodHouseContext(Configuration.GetConnectionString("DefaultConnection")))
{
var features = dbContext.Features.Select(s => s.Name).ToList();
foreach (var feature in features)
{
options.AddPolicy(feature, policy => policy.Requirements.Add(new FeatureRequirement(feature)));
}
}
});
// Configure JWT Token Settings
var jwtAppSettingOptions = Configuration.GetSection(nameof(JwtIssuerOptions));
services.Configure<JwtIssuerOptions>(options =>
{
options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)];
options.SigningCredentials = new SigningCredentials(_signingKey, SecurityAlgorithms.HmacSha256);
});
#endregion
}
Everything is working fine - the Token is been issued and Authorize is working fine, when I make an UnAuthenticated request I get the following response:
As you can see the Status = 401, which means UnAuthenticated - But as you can see the StatusText is empty.
Now there are many reasons why the request can be UnAuthenticated e.g. JWT Token expired , what I want to do is pass a custom Status Text based on the Validation rule failing on the server, how can I do this in MVC Core?

Resources