[Question] - How to properly setup MVC5 as IdentityServer Client?
See original GitHub issue-
Abp package version: 5.10.1.0
-
Base framework: .Net Framework ExternalLogin * Steps needed to reproduce the problem:
- Download a new ASP.NET MVC 5.x - Multi-Page Web App.
- Integrate Code PKCE Flow (2020) like Scott Brady suggest in his blog
- At this point your app when started will redirect to IdentityProvider - I’m currentily using a demo identity-server for practicality.
- Login use either bob/bob, alice/alice or your Google account
- Now when the IdentityServer redirect to my app it’s crashes because the user does not exists in
AbpUser
table
For convinience, this is how my startup class looks like:
using System;
using System.Configuration;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Abp.Owin;
using Coders.MVC5.Api.Controllers;
using Coders.MVC5.Web;
using IdentityModel;
using Microsoft.AspNet.Identity;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.Notifications;
using Microsoft.Owin.Security.OpenIdConnect;
using Owin;
[assembly: OwinStartup(typeof(Startup))]
namespace Coders.MVC5.Web
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseAbp();
//app.UseOAuthBearerAuthentication(AccountController.OAuthBearerOptions);
//app.UseCookieAuthentication(new CookieAuthenticationOptions
//{
// AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
// LoginPath = new PathString("/Account/Login"),
// // by setting following values, the auth cookie will expire after the configured amount of time (default 14 days) when user set the (IsPermanent == true) on the login
// ExpireTimeSpan = new TimeSpan(int.Parse(ConfigurationManager.AppSettings["AuthSession.ExpireTimeInDays.WhenPersistent"] ?? "14"), 0, 0, 0),
// SlidingExpiration = bool.Parse(ConfigurationManager.AppSettings["AuthSession.SlidingExpirationEnabled"] ?? bool.FalseString)
//});
//app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseKentorOwinCookieSaver();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "cookie"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = "interactive.confidential",
Authority = "https://demo.identityserver.io/",
RedirectUri = "https://localhost:44360/",
Scope = "openid profile",
SignInAsAuthenticationType = "cookie",
RequireHttpsMetadata = false,
UseTokenLifetime = false,
RedeemCode = true,
SaveTokens = true,
ClientSecret = "secret",
ResponseType = "code",
ResponseMode = "query",
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication)
{
// generate code verifier and code challenge
var codeVerifier = CryptoRandom.CreateUniqueId(32);
string codeChallenge;
using (var sha256 = SHA256.Create())
{
var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
codeChallenge = Base64Url.Encode(challengeBytes);
}
// set code_challenge parameter on authorization request
n.ProtocolMessage.SetParameter("code_challenge", codeChallenge);
n.ProtocolMessage.SetParameter("code_challenge_method", "S256");
// remember code verifier in cookie (adapted from OWIN nonce cookie)
// see: https://github.com/scottbrady91/Blog-Example-Classes/blob/master/AspNetFrameworkPkce/ScottBrady91.BlogExampleCode.AspNetPkce/Startup.cs#L85
RememberCodeVerifier(n, codeVerifier);
}
return Task.CompletedTask;
},
AuthorizationCodeReceived = n =>
{
// get code verifier from cookie
// see: https://github.com/scottbrady91/Blog-Example-Classes/blob/master/AspNetFrameworkPkce/ScottBrady91.BlogExampleCode.AspNetPkce/Startup.cs#L102
var codeVerifier = RetrieveCodeVerifier(n);
// attach code_verifier on token request
n.TokenEndpointRequest.SetParameter("code_verifier", codeVerifier);
return Task.CompletedTask;
}
}
});
app.MapSignalR();
//ENABLE TO USE HANGFIRE dashboard (Requires enabling Hangfire in MVC5WebModule)
//app.UseHangfireDashboard("/hangfire", new DashboardOptions
//{
// Authorization = new[] { new AbpHangfireAuthorizationFilter() } //You can remove this line to disable authorization
//});
}
private void RememberCodeVerifier(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> n, string codeVerifier)
{
var properties = new AuthenticationProperties();
properties.Dictionary.Add("cv", codeVerifier);
n.Options.CookieManager.AppendResponseCookie(
n.OwinContext,
GetCodeVerifierKey(n.ProtocolMessage.State),
Convert.ToBase64String(Encoding.UTF8.GetBytes(n.Options.StateDataFormat.Protect(properties))),
new CookieOptions
{
SameSite = SameSiteMode.None,
HttpOnly = true,
Secure = n.Request.IsSecure,
Expires = DateTime.UtcNow + n.Options.ProtocolValidator.NonceLifetime
});
}
private string RetrieveCodeVerifier(AuthorizationCodeReceivedNotification n)
{
string key = GetCodeVerifierKey(n.ProtocolMessage.State);
string codeVerifierCookie = n.Options.CookieManager.GetRequestCookie(n.OwinContext, key);
if (codeVerifierCookie != null)
{
var cookieOptions = new CookieOptions
{
SameSite = SameSiteMode.None,
HttpOnly = true,
Secure = n.Request.IsSecure
};
n.Options.CookieManager.DeleteCookie(n.OwinContext, key, cookieOptions);
}
var cookieProperties = n.Options.StateDataFormat.Unprotect(Encoding.UTF8.GetString(Convert.FromBase64String(codeVerifierCookie)));
cookieProperties.Dictionary.TryGetValue("cv", out var codeVerifier);
return codeVerifier;
}
private string GetCodeVerifierKey(string state)
{
using (var hash = SHA256.Create())
{
return OpenIdConnectAuthenticationDefaults.CookiePrefix + "cv." + Convert.ToBase64String(hash.ComputeHash(Encoding.UTF8.GetBytes(state)));
}
}
}
}
This is the error showed in app error page
EDIT
I saw has a method in AccountController called ExternalLogin but IDK if I need to use it
Issue Analytics
- State:
- Created 3 years ago
- Comments:8 (3 by maintainers)
Top Results From Across the Web
asp.net mvc 5 - IdentityServer 4 connect MVC5 as à client
1 Answer 1 ... Exactly the same as you would connect an ASP.NET MVC 5 client to Identity Server 3. You integrate with...
Read more >IdentityServer4 and ASP.NET MVC
To define the ASP.NET MVC application as a client for IdentityServer we need to provide its information using the Client object. Before this ......
Read more >IdentityServer4 in ASP.NET Core - Ultimate Beginner's Guide
In this article, we will start learning about IdentityServer4 in ASP.NET Core and ways to integrate it to build secure solutions.
Read more >IdentityServer3
IdentityServer is configured in the startup class. Here we provide information about the clients, users, scopes, the signing certificate and some other ...
Read more >Refreshing your Legacy ASP.NET IdentityServer Client ...
Give your ASP.NET 4.x apps a refresh with the latest OWIN updates and Proof Key for Code Exchange.
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
@behiunforgiven I moved to another framework, but I understood what I was doing wrong a few months ago.
Actually when you are redirected back (from IdentityProvider to ABP) you need to check if the user exists on ABP and then insert or update the user information.
After you made the TokenRequest, you should deserialize the JWT and get the user’s claims. With the claims you could now use the Subject Claim to check it in User table and if it doesn’t exists create a new record, or if it exists finishes or update the user information.
Something like this. (PS. Javascript code)
@mirusky Did you solve this problem?