Identity Server 4 with EF identity DB - OpenID Connect Failing - asp.net-mvc

PLEASE NOTE: This issue was not resolved in this post. I was asked to create a new post. Please refer to the new post titled:
Identity Server 4 with EF identity DB - OpenID Connect Failing (1)
I have an Identity Server 4 solution with EF Identity DB. I can login with my email and external gmail account, but when I try to login using OpenID (User name and Password) I receive the error below. The issue maybe with the info stored in the Identity DB tables. I'm new to Identity Server and this is my first attempt working with EF Identity DB. I can post DB info if it helps resolve the issue.
Source code:
https://github.com/gotnetdude/GotNetDude-PublicRepository/tree/master/AuthServer
Identity Server Log File:
https://github.com/gotnetdude/GotNetDude-PublicRepository/blob/master/AuthServer_log.txt
MVC Client Log:
https://github.com/gotnetdude/GotNetDude-PublicRepository/blob/master/MVCClient_log.txt
Any suggestions would be appreciated. Paul
EDIT
Please find below the Client configuration info from the identity tables. I'm not sure where to set the AllowedRedirectUris in the DB. The other question I have is why does it work when I sign-in with my email account?
Here is the AuthServer Startup code where I add oidc mvc client as the challenge option ("OpenID Connect") which is fail. The MVC client works fine if I login with the email credentials. I guess is that this has some to do with the way the scope is being handled on the mvc client. Any suggestion are appreciated.
services.AddAuthentication()
.AddGoogle("Google", options =>
{
options.ClientId = "434483408261-55tc8n0cs4ff1fe21ea8df2o443v2iuc.apps.googleusercontent.com";
options.ClientSecret = "3gcoTrEDPPJ0ukn_aYYT6PWo";
})
.AddOpenIdConnect("oidc", "OpenID Connect", options =>
{
//options.Authority = "https://demo.identityserver.io/";
//options.ClientId = "implicit";
//options.SaveTokens = true;
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.SaveTokens = true;
options.ClientId = "mvc";
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
};
});
I'm trying to connect to authority (:5000) from the MVC Client (:5002) see image below:
Debug results from AccountService:
There are 3 instances IdentityServer4.EntityFramework.Entities.Client in the client store under context, clients. All 3 instances had there EnableLocalLogin set to True. I hit the break point after selecting OpenID Connect option.
I also set a breakpoint at the top of the login controller, which it never reached:

I think the problem is in line 74 from AuthServer/Startup:
.AddOpenIdConnect("oidc", "OpenID Connect", options =>
{
...
options.ClientId = "mvc";
...
});
The server is not the mvc client. I think this is 'confusing' IdentityServer. You don't need to add oidc to the server. If you remove theses lines then it should work.
If you login from the mvc client website (:5002) then you should be redirected. And if you login to IdentityServer (:5000) you don't have to be redirected. The server is the authority, the resource is identified by a scope and the client by a clientid.

Check the Log files that you have provided. The error that you are receiving is
Invalid redirect_uri: http://localhost:5000/signin-oidc.
If you check your client configuration your AllowedRedirectUris contains http://localhost:5002/signin-oidc.
You have a (typo) mistake in the port. It must be 5002.
EDIT
According to your screenshots, and to the log files, your client is properly configured on the Identity Server side. The problem is in your MVC client, not in the database. You need to look there, and find what RedirectUrl you are setting when starting the client itself.
EDIT 2:
OK, after looking at your code I realized that what #Ruard van Elburg is telling you is the reason for having this problem. When using the Internal authentication, you don't need to specify it like this (you are really confusing Identity Server). This specification is for external Oidc provider only (for example Okta, or whatever else Oidc provider you have). Check here. You see - the Identity Server Startup.cs doesn't contain this code that you have (line 74 to 89 here). Why don't we do this step by step. Try with removing the lines that I mentioned.

Related

How to make secure authentication for .NET Core Web API?

I am developing an app with .NET Core Web API, Entity Framework and React. I've been reading a lot recently about possible authentication techniques for my API and I've discovered that plain JWT is not entirely secure, so at first I decided to use OpenID Connect with IdentityServer 4. I understand the idea behind OAuth 2.0 and OpenID Connect is to hide user credentials during login process and to involve external authentication provider in issuing an access token, but I don't want to rely on such services because not everyone have an account on Facebook etc. I consider this as an optional way to login. I want to give users an ability to sign in with just login and password. So what is the best (secure) way to accomplish this in modern web apps?
Having project 1 as Client App, project 2 as API Resources and project 3 as Authorization Service (IdentityServer4), I consider following scenarios:
A user is able to create an account on Authorization Service which is responsible for issuing a token required to get access to API Resources through Client App. Authorization Service is registered as authorization provider only for my Client App.
Get authorization token from Authorization Service using resource owner password grant - this one is not recommended by the specs but in my case since user must provide credentials to Authorization Service anyway and I will be hosting every project I can't see any problem.
Don't bother with OAuth and implement authorization mechanism using ASP.NET Core Identity + bearer token authentication.
Any ideas or recommendations highly apprecieated.
I use the JwtBearer package, wire it up in your Startup.cs Configure method like
.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration["AppSettings:AuthConfig:SecretKey"])),
ValidateIssuer = true,
ValidIssuer = Configuration["AppSettings:AuthConfig:Issuer"],
ValidateAudience = true,
ValidAudience = Configuration["AppSettings:AuthConfig:Audience"],
ValidateLifetime = true,
}
})
and my login action on my User controller looks like
[HttpPost]
public string Post([FromBody]LoginRequest request)
{
var contact = dbContext.Contacts.Where(c => c.Active && c.Email == request.Email).Select(c => new { c.Id, c.PasswordHash }).SingleOrDefault();
if (contact == null || !Security.PasswordHash.ValidatePassword(request.Password, contact.PasswordHash))
{
return string.Empty;
}
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(appSettings.AuthConfig.SecretKey));
var now = DateTime.UtcNow;
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, contact.Id.ToString()),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.Now.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
};
var jwt = new JwtSecurityToken(
issuer: appSettings.AuthConfig.Issuer,
audience: appSettings.AuthConfig.Audience,
claims: claims,
notBefore: now,
expires: now.AddDays(30),
signingCredentials: new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256));
jwt.Payload.Add("roles", dbContext.ContactRoles.Where(cr => cr.ContactId == contact.Id).Select(ur => ur.Role.Name).ToArray());
return new JwtSecurityTokenHandler().WriteToken(jwt);
}
I use a JWT package for Angular on the client, there may be something similar for React.

Cannot Signout the External Identity provider in IdentityServer

I've an MVC Application which uses IdentityServer4. In IdentityServer4, I registered SAML2 (SustainSys.SAML2) as the external Login provider. and Login works fine.
When user log out of the MVC application, it logs out from the MVC application but the log out for External Login Provider isn't triggering. I checked the LogOut method of my identity Server which does the redirect to External Authentication Scheme. but the redirect doesnt happen.
this triggers a redirect to the external provider for sign-out
return SignOut(new AuthenticationProperties { RedirectUri = url },
vm.ExternalAuthenticationScheme);
And here is the code where in i registered External Identity Provider for SAML. I've used Nuget package from SustainSys SAML.
.AddSaml2(options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.SignOutScheme = IdentityServerConstants.SignoutScheme;
options.SPOptions = CreateSPOptions();
var idp = new IdentityProvider(new EntityId(_strIDPEntityId), options.SPOptions)
{
AllowUnsolicitedAuthnResponse = true,
Binding = Saml2BindingType.HttpRedirect,
SingleSignOnServiceUrl = new Uri(_strSingleSignOnURL),
SingleLogoutServiceBinding = Saml2BindingType.HttpRedirect,
SingleLogoutServiceUrl = new Uri("https://devit-dev.onelogin.com/trust/saml2/http-redirect/slo/1111")
};
idp.SigningKeys.AddConfiguredKey(
new X509Certificate2(
AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "../../../App_Data/OneLogin.cer"));
options.IdentityProviders.Add(idp);
});
Not sure what am i missing here. Appreciate any help.
Check your logs, it should show you the decision process that ends up in a local logout. There are A LOT of things that need to be in place for a federated logout to work. You need a service certificate and you need some special claims. The latter will be simplified in a future compatibility release with Sustainsys.Saml2/IdSrv4

External Cookie for External Login in ASP.NET OWIN

We have a legacy system which is built on ASP.NET Mvc 4, now we would like to support Signal Sign On via Azure Active Directory for current users as well as new users. Since we have managed our own authentication workflow, ASP.NET Identity definitely does not fit in our case.
I have managed to build a demo which is working on OWIN OpenIdConnect middleware passive mode without using ASP.NET Identity. The below code works correctly:
app.SetDefaultSignInAsAuthenticationType("ExternalCookie");
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "ExternalCookie",
AuthenticationMode = AuthenticationMode.Passive,
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Passive,
ClientId = ClientId,
Authority = Authority
// More code
});
And in ExternalLoginCallback action:
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var authManager = Request.GetOwinContext().Authentication;
var result = await authManager.AuthenticateAsync("ExternalCookie");
authManager.SignOut("ExternalCookie");
//More code to convert to local identity
}
This case is really common even using other providers like Google, Facebook or Twitter. One thing I have not much clear is ExternalCookie, maybe I have missed the whole thing. My understanding is when external login is successfully, external cookie is used to store the external claim identity. And then we call:
var result = await authManager.AuthenticateAsync("ExternalCookie");
authManager.SignOut("ExternalCookie");
In order to get the external claim identity and then convert external identity to local identity. I have a little bit confusion why we have to call SignOut external cookie in this case.
Also, I'm not sure whether External Cookie is a must when using external login, or do we have other ways around without using External Cookie.
Please someone give an explanation on this point.
To answer your last question, you change the name of cookie in startup.auth file where you configure external cookie -
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
You can use a string instead of DefaultAuthenticationTypes enum and directly specify the name of the cookie like -
app.UseExternalSignInCookie("myExternalCookie");

Azure Active Directory Sign out Error

I've created a generic website from visual studio 2013 and successfully wired it up to an existing Azure Active Directory instance for authentication. I can login as any user in the Azure AD with the appropriate credentials. Unfortunately I can not sign out without receiving this error:
AADSTS50068: Signout failed. The initiating application is not a
participant in the current session.
I've googled the error number, but apparently I'm the first person to ever encounter this....:) I'm pretty sure I'm not, but I'm now at a loss to explain why sign in works, but sign out does not.
Here is the sign out code, pretty much exactly as it was generated:
public ActionResult SignOut()
{
WsFederationConfiguration config = FederatedAuthentication.FederationConfiguration.WsFederationConfiguration;
// Redirect to SignOutCallback after signing out.
string callbackUrl = Url.Action("SignOutCallback", "Account", routeValues: null, protocol: Request.Url.Scheme);
SignOutRequestMessage signoutMessage = new SignOutRequestMessage(new Uri(config.Issuer), callbackUrl);
signoutMessage.SetParameter("wtrealm", IdentityConfig.Realm ?? config.Realm);
string signoutMsg = signoutMessage.WriteQueryString();
FederatedAuthentication.SessionAuthenticationModule.SignOut();
return new RedirectResult(signoutMsg);
}
After deleting the website in Azure and the associated live.com registration for Microsoft OAuth (https://account.live.com/developers/applications/) I recreated the example and logout worked as expected. I'm pretty confident I had incorrectly registered the application with an invalid "Redirect Url" (http vs https).

OWIN OAuth server sample code example

I am using the sample code from this OWIN project http://www.asp.net/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server and I didnt the deploy of the solutions .When I try to get a authorization token from the server I get redirected to the login page and then to the authorize page but when I am clicking on grant the page is not rediretcing me ..Checking the code it is this am I supposed to create the redirect login with access token myself or is there some configuration I am missing here .
If anyone familiar with the project can please let me know.
if (!string.IsNullOrEmpty(Request.Form.Get("submit.Grant")))
{
identity = new ClaimsIdentity(identity.Claims, "Bearer", identity.NameClaimType, identity.RoleClaimType);
foreach (var scope in scopes)
{
identity.AddClaim(new Claim("urn:oauth:scope", scope));
}
authentication.SignIn(identity);

Resources