Working with OpenId protocol - c#

I am having some trouble authenticating with OpenId Connect.
I set breakpoints in the events for the OpenId and after the authentication process I am only getting a MessageReceived (at least I am receiving the token…) and consequently I am not successful authenticated! I think I should be receiving the access token in the AuthorizationCodeReceived.
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string userObjectID = context.AuthenticationTicket.Identity.FindFirst(
"http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new NaiveSessionCache(userObjectID));
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);
token = result.AccessToken;
return Task.FromResult(0);
},
AuthenticationFailed = (context) =>
{
//context.OwinContext.Response.Redirect("/Home/Error");
//context.HandleResponse(); // Suppress the exception
return Task.FromResult(0);
},
MessageReceived = (context) =>
{
return Task.FromResult(0);
},
SecurityTokenReceived = (context) =>
{
return Task.FromResult(0);
},
SecurityTokenValidated = (context) =>
{
return Task.FromResult(0);
}
}
});
Any idea about why this behaviour is happening?

In my case it was related with the Katana library (MS owin). It is necessary to override the CookieManager class in order to force OWIN to use the .NET cookie.
public class SystemWebCookieManager : ICookieManager
{
public string GetRequestCookie(IOwinContext context, string key)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
var webContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
var cookie = webContext.Request.Cookies[key];
return cookie == null ? null : cookie.Value;
}
public void AppendResponseCookie(IOwinContext context, string key, string value, CookieOptions options)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
if (options == null)
{
throw new ArgumentNullException("options");
}
var webContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
bool domainHasValue = !string.IsNullOrEmpty(options.Domain);
bool pathHasValue = !string.IsNullOrEmpty(options.Path);
bool expiresHasValue = options.Expires.HasValue;
var cookie = new HttpCookie(key, value);
if (domainHasValue)
{
cookie.Domain = options.Domain;
}
if (pathHasValue)
{
cookie.Path = options.Path;
}
if (expiresHasValue)
{
cookie.Expires = options.Expires.Value;
}
if (options.Secure)
{
cookie.Secure = true;
}
if (options.HttpOnly)
{
cookie.HttpOnly = true;
}
webContext.Response.AppendCookie(cookie);
}
public void DeleteCookie(IOwinContext context, string key, CookieOptions options)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
if (options == null)
{
throw new ArgumentNullException("options");
}
AppendResponseCookie(
context,
key,
string.Empty,
new CookieOptions
{
Path = options.Path,
Domain = options.Domain,
Expires = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc),
});
}
}
And then in the startup class:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
...
CookieManager = new SystemWebCookieManager()
...
});

Related

ASP.NET Core - getting a message from AuthenticateResult.Fail

Can not get an error message in HTTP response.
if (!string.IsNullOrWhiteSpace(checkTokenResponse.Error))
{
Logger.LogError(checkTokenResponse.Error);
return AuthenticateResult.Fail(checkTokenResponse.Error);
}
response
I suppose the error message should render into data response field
The same question ASP.NET Core - getting a message back from AuthenticationHandler
Update
code of custom Auth handler
public class AuthHandler : AuthenticationHandler<AuthOptions>
{
private readonly IPrimeApiProxy _primeApiProxy;
private readonly IPrimeProxy _primeProxy;
private readonly int _adminGroupId;
private static readonly string HeaderName = "Authorization";
private static readonly string ApiAuthScheme = "oauth ";
public AuthHandler(
IOptionsMonitor<AuthOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
IPrimeApiProxy primeApiProxy,
IPrimeProxy primeProxy,
ApplicationSettingsProvider settings)
: base(options, logger, encoder, clock)
{
_primeApiProxy = primeApiProxy ?? throw new ArgumentNullException(nameof(primeApiProxy));
_primeProxy = primeProxy ?? throw new ArgumentNullException(nameof(primeProxy));
_adminGroupId = int.Parse((settings as dynamic).AdminGroupId as string);
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
Logger.LogTrace("HandleAuthenticateAsync");
if (!Request.Headers.ContainsKey("Authorization"))
{
return AuthenticateResult.Fail("Missing Authorization Header");
}
try
{
var token = FetchToken(Request);
if (string.IsNullOrWhiteSpace(token.token) && token.userId < 1)
{
Logger.LogError("Invalid token");
return AuthenticateResult.Fail("Invalid token");
}
var checkTokenResponse = await _primeProxy.CheckToken(token.token);
if (!string.IsNullOrWhiteSpace(checkTokenResponse.Error))
{
Logger.LogError(checkTokenResponse.Error);
return AuthenticateResult.Fail(checkTokenResponse.Error);
}
var isValidInt = int.TryParse(checkTokenResponse.UserId, out var userId);
if (!isValidInt)
{
return AuthenticateResult.Fail("User Id is invalid");
}
if (token.userId != userId)
{
return AuthenticateResult.Fail("The token belongs to another user");
}
bool isUserAdminAndCustomGroupMember = false;
if (checkTokenResponse.UserRole == "admin")
{
isUserAdminAndCustomGroupMember = await _primeApiProxy.IsGroupMember(token.token, token.userId, _adminGroupId);
}
var claims = new List<Claim>()
{
new Claim(ClaimTypes.NameIdentifier, token.userId.ToString()),
new Claim(ClaimTypes.Role, isUserAdminAndCustomGroupMember ? "Admin" : "Learner")
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
catch (Exception ex)
{
Logger.LogError(ex, "Auth exception");
return AuthenticateResult.Fail("Invalid Authorization Header");
}
}
private static (string token, int userId) FetchToken(HttpRequest request)
{
string authHeader = request.Headers[HeaderName];
if (authHeader != null && authHeader.StartsWith(ApiAuthScheme, StringComparison.OrdinalIgnoreCase))
{
string token = authHeader.Substring(ApiAuthScheme.Length).Trim();
string[] parts = token.Split(',', StringSplitOptions.RemoveEmptyEntries);
if (int.TryParse(parts[1], out int userId))
{
return (parts[0], userId);
}
}
return (null, 0);
}
}

How do you solve the error “could not load file or assembly 'microsoft aspnetcore razor runtime 3.1 1”,

I've posted this question before but being new I wanted to learn how to write a "proper" question (in case you've tried to help me and I didn't get back immediately, wanted to get it mostly right if not all hopefully I've got the hang of it)
This is just registration/login code whose purpose is self explanatory, the error am getting (could not load file or assembly 'microsoft aspnetcore razor runtime 3.1 1) happens in a service registration class in the AddControllers() method, I've tried adding the package specified in the error but it didn't work, I tried a few other similar packages but the result has been the same, when I build I get no errors or warnings but when the app runs I get the error, hope this is enough data to work with.
//controller class Login and registration
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class IdentifyMe : Controller
{
private readonly IIdentify _identify;
public IdentifyMe(IIdentify identifying)
{
_identify = identifying;
}
[HttpGet(Api_Routes.Identity.Register)]
public async Task<IActionResult> Register(UserRegistration register)
{
if (!ModelState.IsValid)
{
return BadRequest(new Unauthenticated
{
Errors=ModelState.Values.SelectMany(x=>x.Errors.Select(xx=>xx.ErrorMessage))
});
}
var authresponce = await _identify.RegisterAsync(register.Email, register.Password, register.User_Name);
if (!authresponce.Success)
{
return BadRequest(new Unauthenticated
{
Errors = authresponce.Errors
});
}
return Ok(new Authenticated
{
Token = authresponce.Token
});
}
[HttpGet(Api_Routes.Identity.Login)]
public async Task<IActionResult> LoginAsync(User_login login)
{
var authresponce = await _identify.LoginAsync(login.Password, login.email);
if (!authresponce.Success)
{
return BadRequest(new Unauthenticated
{
Errors = authresponce.Errors
});
}
return Ok(new Authenticated
{
Token = authresponce.Token
});
}
}
// service registration
public class Dbinstaller : IInstaller
{
public void Cleanner(IConfiguration configuration, IServiceCollection services)
{
var jwt = new JWTsettings();
configuration.Bind(nameof(jwt), jwt);
services.AddSingleton(jwt);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.SaveToken = true;
var TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwt.Secret)),
ValidateIssuer = false,
ValidateAudience = false,
RequireExpirationTime = false,
ValidateLifetime = true
};
});
services.AddSwaggerGen(x =>
{
x.SwaggerDoc("v1", new OpenApiInfo { Title = "TXC API", Version = "v1" });
var security = new Dictionary<string, IEnumerable<string>>
{
{"Bearer", new string[0]}
};
x.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description="JWT Authorization Header using the bearer scheme",
Name="Authorization",
In=ParameterLocation.Header,
Type=SecuritySchemeType.ApiKey
});
x.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{new OpenApiSecurityScheme{Reference=new OpenApiReference
{
Id="Bearer",
Type=ReferenceType.SecurityScheme
}
},new List<string>() }
});
});
services.AddDbContext<DataContext>(opt =>
opt.UseSqlServer(configuration.GetConnectionString("TXC Connection")));
services.AddIdentityCore<IdentityUser>();
}
}
//service registration, specified error occurs in Add controllers()
public class In_App_Componentes : IInstaller
{
public void Cleanner(IConfiguration configuration, IServiceCollection services)
{
services.AddControllers().AddNewtonsoftJson(p => p.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver());
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
services.AddScoped<IX_Change, The_center>();
services.AddScoped<IIdentify, Identify>();
}
}
//service implementation class
public class Identify : IIdentify
{
private readonly UserManager<IdentityUser> _manager;
private readonly JWTsettings jwtset;
public Identify(UserManager<IdentityUser> userManager, JWTsettings jW)
{
_manager = userManager;
jwtset = jW;
}
public async Task<Authentication_result> RegisterAsync(string email, string password, string Username)
{
var exists = _manager.FindByEmailAsync(email);
if (exists != null)
{
return new Authentication_result
{
Errors = new[] { "User with this email already exists" }
};
}
var newPerson = new IdentityUser
{
Email = email,
UserName = Username
};
var Creation = await _manager.CreateAsync(newPerson, password);
if (!Creation.Succeeded)
{
return new Authentication_result
{
Errors = new[] { "Invalid user!" }
};
}
return Generate_Authentication_Result(newPerson);
}
public async Task<Authentication_result> LoginAsync(string email, string Password)
{
var user = await _manager.FindByEmailAsync(email);
if (user == null)
{
return new Authentication_result
{
Errors = new[] { "User does not exists" }
};
}
var validate_password = await _manager.CheckPasswordAsync(user, Password);
if (!validate_password)
{
return new Authentication_result
{
Errors = new[] { "" }
};
}
return Generate_Authentication_Result(user);
}
private Authentication_result Generate_Authentication_Result(IdentityUser newPerson)
{
var Tokenhandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(jwtset.Secret);
var TokenDescripter = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(JwtRegisteredClaimNames.Sub, newPerson.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Email, newPerson.Email),
new Claim("id",newPerson.Id)
}),
Expires = DateTime.UtcNow.AddHours(2),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = Tokenhandler.CreateToken(TokenDescripter);
return new Authentication_result
{
Success = true,
Token = Tokenhandler.WriteToken(token)
};
}
}
Ciao, I tried to search Microsoft.AspNetCore.Razor.Runtime v. 3.1.1 on NuGet but I found only 2.2.0 . I didn't found any reference to 3.1.1 . Anyway, downgrading Microsoft.AspNetCore.Identity.UI to 3.1.0 should solve your problem as mentioned here

C# OWIN Security - set expiration token - always have default value

hello everyone I currently have a project where I use owin security
when I try to make the request to / token I get this
there specifies that the expiration token is at 7199 seconds (2 hours)
I was looking for this endpoint (route) but I did not fond /token it or find the place where they set this value to 2 hours (look in the whole solution)
The only thing I found was this class that corresponds to the refresh token (but no expiration token) but this token is set to 14400 and yet when I make the request again the token always remains at that value
namespace Conarch.Providers
{
public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
{
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
var clientid = context.Ticket.Properties.Dictionary["as:client_id"];
context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddMinutes(Convert.ToDouble(12000));
if (string.IsNullOrEmpty(clientid))
{
return;
}
var refreshTokenId = Guid.NewGuid().ToString("n");
using (AuthRepository _repo = new AuthRepository())
{
var refreshTokenLifeTime = context.OwinContext.Get<string>("as:clientRefreshTokenLifeTime");
var token = new RefreshToken()
{
Id = Helper.GetHash(refreshTokenId),
ClientId = clientid,
Subject = context.Ticket.Identity.Name,
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddMinutes(Convert.ToDouble(refreshTokenLifeTime))
};
context.Ticket.Properties.IssuedUtc = token.IssuedUtc;
context.Ticket.Properties.ExpiresUtc = token.ExpiresUtc;
token.ProtectedTicket = context.SerializeTicket();
var result = await _repo.AddRefreshToken(token);
if (result)
{
context.SetToken(refreshTokenId);
}
}
}
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
string hashedTokenId = Helper.GetHash(context.Token);
using (AuthRepository _repo = new AuthRepository())
{
var refreshToken = await _repo.FindRefreshToken(hashedTokenId);
if (refreshToken != null )
{
//Get protectedTicket from refreshToken class
context.DeserializeTicket(refreshToken.ProtectedTicket);
var result = await _repo.RemoveRefreshToken(hashedTokenId);
}
}
}
public void Create(AuthenticationTokenCreateContext context)
{
throw new NotImplementedException();
}
public void Receive(AuthenticationTokenReceiveContext context)
{
throw new NotImplementedException();
}
My question is: in what place do you set this value and how could the time increase?
thank you very much
You have to set the expiration time during your web application configuration
Use this:
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new AuthorizationServerProvider(),
RefreshTokenProvider = new RefreshTokenProvider()
};
You may find the full article here

Receiving 400 Bad Request when requesting an access_token with OAuth Authorization Code Flow in C#?

Following Owin OAuth guide implemented the authorization code flow below. Am successfully receiving the authorization code from server, ie. till step D mentioned in the article.
When I request access_token from the server with grant_type=authorization_code. I am getting 400 Bad Request error saying invalid grant
Here's the code:
Startup.Auth.cs
public void ConfigureAuth(IAppBuilder app)
{
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
//use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ExternalCookie);
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
AuthorizeEndpointPath = new PathString("/oauth/authorize"),
TokenEndpointPath = new PathString("/oauth/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(60),
Provider = new SimpleOAuthProvider(),
AuthorizationCodeProvider = new AuthenticationCodeProvider(),
RefreshTokenProvider = new SimpleRefreshTokenProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");
//app.UseTwitterAuthentication(
// consumerKey: "",
// consumerSecret: "");
//app.UseFacebookAuthentication(
// appId: "",
// appSecret: "");
//app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
//{
// ClientId = "",
// ClientSecret = ""
//});
}
AuthorizationCodeProvider
internal class AuthenticationCodeProvider: IAuthenticationTokenProvider
{
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
var code = Guid.NewGuid().ToString("n");
using (AuthRepository _repo = new AuthRepository())
{
var token = new AuthCode()
{
Token = HashProvider.GetHash(code),
IssuedUtc = DateTime.UtcNow
};
context.Ticket.Properties.IssuedUtc = token.IssuedUtc;
token.ProtectedTicket = context.SerializeTicket();
var result = await _repo.AddAuthCodeToken(token);
if (result)
{
context.SetToken(token.Token);
}
}
}
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
var headerPresent = context.OwinContext.Response.Headers.Any(x => x.Key == "Access-Control-Allow-Origin");
if (headerPresent)
{
context.OwinContext.Response.Headers.Remove("Access-Control-Allow-Origin");
}
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
string hashedToken = context.Token;
hashedToken = Uri.UnescapeDataString(hashedToken);
using (AuthRepository _repo = new AuthRepository())
{
var authCode = await _repo.FindAuthCodeToken(hashedToken);
if (authCode != null)
{
//Get protectedTicket from refreshToken class
context.DeserializeTicket(authCode.ProtectedTicket);
var result = await _repo.RemoveAuthCodeToken(hashedToken);
}
}
}
public void Create(AuthenticationTokenCreateContext context)
{
throw new NotImplementedException();
}
public void Receive(AuthenticationTokenReceiveContext context)
{
throw new NotImplementedException();
}
}
OAuthAuthorizationServerProvider
public class SimpleOAuthProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
//Check if client details are valid
string clientId = string.Empty;
string clientSecret = string.Empty;
AuthClient client = null;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (context.ClientId == null)
{
//Remove the comments from the below line context.SetError, and invalidate context
//if you want to force sending clientId/secrects once obtain access tokens.
context.Validated();
//context.SetError("invalid_clientId", "ClientId should be sent.");
return Task.FromResult<object>(null);
}
using (AuthRepository _repo = new AuthRepository())
{
client = _repo.FindClient(context.ClientId);
}
if (client == null)
{
context.SetError("invalid_clientId", string.Format("Client '{0}' is not registered in the system.", context.ClientId));
return Task.FromResult<object>(null);
}
if (client.Type == (Int16)ClientTypeEnum.Native)
{
if (string.IsNullOrWhiteSpace(clientSecret))
{
context.SetError("invalid_clientId", "Client secret should be sent.");
return Task.FromResult<object>(null);
}
else
{
if (client.Secret != clientSecret)
{
context.SetError("invalid_clientId", "Client secret is invalid.");
return Task.FromResult<object>(null);
}
}
}
if (!client.Active)
{
context.SetError("invalid_clientId", "Client is inactive.");
return Task.FromResult<object>(null);
}
context.OwinContext.Set<string>("as:clientAllowedOrigin", client.AllowedOrigin);
context.OwinContext.Set<string>("as:clientRefreshTokenLifeTime", client.RefreshTokenLifeTime.ToString());
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
if (allowedOrigin == null) allowedOrigin = "*";
var headerPresent = context.OwinContext.Response.Headers.Any(x => x.Key == "Access-Control-Allow-Origin");
if (headerPresent)
{
context.OwinContext.Response.Headers.Remove("Access-Control-Allow-Origin");
}
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
using (AuthRepository _repo = new AuthRepository())
{
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
if (!user.EmailConfirmed)
{
context.SetError("unconfirmed_email", "Please confirm your email id.");
return;
}
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim("FullName", user.FullName));
identity.AddClaim(new Claim(ClaimTypes.Role, "user"));
identity.AddClaim(new Claim("sub", context.UserName));
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"client_id", (context.ClientId == null) ? string.Empty : context.ClientId
},
{
"session_id", Guid.NewGuid().ToString("n")
},
{
"email", user.Email
},
{
"user_id", user.Id.ToString()
},
{
"fullname", user.FullName.ToString()
}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}
public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
var originalClient = context.Ticket.Properties.Dictionary["client_id"];
var currentClient = context.ClientId;
if (originalClient != currentClient)
{
context.SetError("invalid_clientId", "Refresh token is issued to a different clientId.");
return Task.FromResult<object>(null);
}
// Change auth ticket for refresh token requests
var newIdentity = new ClaimsIdentity(context.Ticket.Identity);
//var newClaim = newIdentity.Claims.Where(c => c.Type == "newClaim").FirstOrDefault();
//if (newClaim != null)
//{
// newIdentity.RemoveClaim(newClaim);
//}
//newIdentity.AddClaim(new Claim("newClaim", "newValue"));
var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties);
context.Validated(newTicket);
return Task.FromResult<object>(null);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
//Authorization Code flow
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
string clientId = context.ClientId;
AuthClient client = null;
using (AuthRepository _repo = new AuthRepository())
{
client = _repo.FindClient(context.ClientId);
}
if (client == null)
{
context.SetError("invalid_clientId", string.Format("Client '{0}' is not registered in the system.", context.ClientId));
return Task.FromResult<object>(null);
}
context.Validated(context.RedirectUri);
return Task.FromResult<object>(null);
}
public override async Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
{
var identity = new ClaimsIdentity(new GenericIdentity(
context.ClientId, OAuthDefaults.AuthenticationType),
context.Scope.Select(x => new Claim("urn:oauth:scope", x)));
context.Validated(identity);
}
public override async Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
{
//Generate auth code to be sent to oauth/token endpoint for getting access_token
if (context.AuthorizeRequest.IsImplicitGrantType)
{
var identity = new ClaimsIdentity("Bearer");
context.OwinContext.Authentication.SignIn(identity);
context.RequestCompleted();
}
else if (context.AuthorizeRequest.IsAuthorizationCodeGrantType)
{
var redirectUri = context.Request.Query["redirect_uri"];
var clientId = context.Request.Query["client_id"];
var userId = context.Request.Query["user_id"];
var state = context.Request.Query["state"];
var scope = context.Request.Query["scope"];
var sessionId = Guid.NewGuid().ToString("n");
var identity = new ClaimsIdentity(new GenericIdentity(
clientId, OAuthDefaults.AuthenticationType));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userId.ToString()));
var authorizeCodeContext = new AuthenticationTokenCreateContext(
context.OwinContext,
context.Options.AuthorizationCodeFormat,
new AuthenticationTicket(
identity,
new AuthenticationProperties(new Dictionary<string, string>
{
{"client_id", clientId},
{"user_id",userId },
{"session_id", sessionId },
{"redirect_uri", redirectUri}
})
{
IssuedUtc = DateTimeOffset.UtcNow,
ExpiresUtc = DateTimeOffset.UtcNow.Add(context.Options.AuthorizationCodeExpireTimeSpan)
}));
await context.Options.AuthorizationCodeProvider.CreateAsync(authorizeCodeContext);
//Set Cors
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
if (allowedOrigin == null) allowedOrigin = "*";
var headerPresent = context.OwinContext.Response.Headers.Any(x => x.Key == "Access-Control-Allow-Origin");
if (headerPresent)
{
context.OwinContext.Response.Headers.Remove("Access-Control-Allow-Origin");
}
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
var model = new { RedirectUri = redirectUri + "?code=" + Uri.EscapeDataString(authorizeCodeContext.Token) + "&state=" + state + "&scope=" + scope };
var json = Newtonsoft.Json.JsonConvert.SerializeObject(model);
context.Response.Write(json);
context.RequestCompleted();
}
}
/// <summary>
/// Verify the request for authorization_code
/// </summary>
public override async Task ValidateAuthorizeRequest(OAuthValidateAuthorizeRequestContext context)
{
if ((context.AuthorizeRequest.IsAuthorizationCodeGrantType || context.AuthorizeRequest.IsImplicitGrantType))
{
context.Validated();
}
else
{
context.Rejected();
}
}
//public override async Task GrantAuthorizationCode(OAuthGrantAuthorizationCodeContext context)
//{
// var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
// if (allowedOrigin == null) allowedOrigin = "*";
// var headerPresent = context.OwinContext.Response.Headers.Any(x => x.Key == "Access-Control-Allow-Origin");
// if (headerPresent)
// {
// context.OwinContext.Response.Headers.Remove("Access-Control-Allow-Origin");
// }
// context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
// var userIdTemp = context.Ticket.Identity.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier);
// if(userIdTemp == null)
// {
// }
// long userId = Convert.ToInt64(userIdTemp.Value);
// var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
// ApplicationUser user = await userManager.FindByIdAsync(userId);
// var identity = new ClaimsIdentity(context.Options.AuthenticationType);
// identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
// identity.AddClaim(new Claim(ClaimTypes.Name, user.UserName));
// identity.AddClaim(new Claim("FullName", user.FullName));
// identity.AddClaim(new Claim(ClaimTypes.Role, "user"));
// //identity.AddClaim(new Claim("sub", context.UserName));
// var props = new AuthenticationProperties(new Dictionary<string, string>
// {
// //{
// // "client_id", (context.Ticket.Properties.Dictionary.FirstOrDefault.ClientId == null) ? string.Empty : context.ClientId
// //},
// {
// "session_id", Guid.NewGuid().ToString("n")
// },
// {
// "email", user.Email
// },
// {
// "user_id", user.Id.ToString()
// },
// {
// "fullname", user.FullName.ToString()
// }
// });
// var ticket = new AuthenticationTicket(identity, props);
// context.Validated(ticket);
//}
}
It seems the middleware will check if the key redirect_uri exists in the dictionary of AuthenticationProperties, remove it and everything works fine(with validated context).
A simplified example of AuthorizationCodeProvider woubld be like so:
public class AuthorizationCodeProvider:AuthenticationTokenProvider {
public override void Create(AuthenticationTokenCreateContext context) {
context.SetToken(context.SerializeTicket());
}
public override void Receive(AuthenticationTokenReceiveContext context) {
context.DeserializeTicket(context.Token);
context.Ticket.Properties.Dictionary.Remove("redirect_uri"); // <-
}
}
And don't forget to make the context validated in the overridden method OAuthAuthorizationServerProvider.ValidateClientAuthentication. Again, here's a simplified example which inherit from the ApplicationOAuthProvider class of the template project:
public partial class DefaultOAuthProvider:ApplicationOAuthProvider {
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context) {
if(null!=context.RedirectUri) {
context.Validated(context.RedirectUri);
return Task.CompletedTask;
}
return base.ValidateClientRedirectUri(context);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) {
if(context.TryGetFormCredentials(out String clientId, out String clientSecret)) {
// Specify the actual expected client id and secret in your case
if(("expected-clientId"==clientId)&&("expected-clientSecret"==clientSecret)) {
context.Validated(); // <-
return Task.CompletedTask;
}
}
return base.ValidateClientAuthentication(context);
}
public DefaultOAuthProvider(String publicClientId) : base(publicClientId) {
}
}
Note that if you invoke context.Validated with a particular client id, then you will have to put the same client_id in the properties of the ticket, you can do that with the method AuthenticationTokenProvider.Receive

With Implicit Flow on IdentityServer4 I am not receiving User data as Name and Claims in IIdentity when making requests with Authentication Bearer

I'm using IdentityServer4 in ASP.NET Core on Framework 4.6.2 with EntityFramework 6 and Asp.Net Identity 2 (not Core).
For this I implemented the IProfileService.
And I am using in my client with Angular 4.x the oidc-client.js library.
I write the access_token in the localStorage to get it and mount the header with the Authentication Bearer.
The problem is that in IPrincipal the Name and Claim are empty. The IsAuthenticated property is false.
My Client configuration:
new Client
{
ClientName = "API Client",
ClientId = IdentityContants.Clients.ImplicitClient.ClientId,
RequireClientSecret = false,
AllowedGrantTypes = GrantTypes.Implicit,
AccessTokenType = AccessTokenType.Jwt,
AllowAccessTokensViaBrowser = true,
AlwaysSendClientClaims = true,
RequireConsent = false,
RedirectUris = { ... },
PostLogoutRedirectUris = { ... },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityContants.Scopes.ApiScope.Name,
}
}
The configuration in API:
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = IdentityContants.AuthServer,
ApiName = IdentityContants.Clients.ImplicitClient.ClientId,
LegacyAudienceValidation = true,
AllowedScopes = new[]
{
"openid",
"profile",
IdentityContants.Scopes.ApiScope.Name
},
RequireHttpsMetadata = false
});
And I have these settings in Angular 4.x applications:
const settings: any = {
authority: environment.urls.api.account,
client_id: environment.clients.api.id,
redirect_uri: environment.urls.front.apps + '/auth',
post_logout_redirect_uri: environment.urls.front.apps,
response_type: 'id_token token',
scope: 'api.scope openid profile',
loadUserInfo: true
};
I'm using JwtClaimTypes.Name and ClaimTypes.Name.
I tried with and without the JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear().
The ProfileService
public class ProfileService : IProfileService
{
private readonly AppUserManager _userManager;
public ProfileService(AppUserManager userManager)
{
_userManager = userManager;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var subject = context.Subject;
if (subject == null) throw new ArgumentNullException(nameof(context.Subject));
var subjectId = subject.GetSubjectId();
var user = await _userManager.Users
.Include( ... the includes )
.SingleOrDefaultAsync(x => x.Id == subjectId);
if (user == null)
throw new ArgumentException("Invalid subject identifier");
context.IssuedClaims = await GetClaimsFromUserAsync(user);
}
public async Task IsActiveAsync(IsActiveContext context)
{
var subject = context.Subject;
if (subject == null) throw new ArgumentNullException(nameof(context.Subject));
var subjectId = subject.GetSubjectId();
var user = await _userManager.Context.Users
.Include( ... the includes )
.SingleOrDefaultAsync(x => x.Id == subjectId);
context.IsActive = await ValidateSecurityStamp(user, subject);
}
private async Task<bool> ValidateSecurityStamp(Usuario user, ClaimsPrincipal subject)
{
if (user == null)
return false;
if (!_userManager.SupportsUserSecurityStamp)
return true;
var securityStamp = subject.Claims.Where(c => c.Type == "security_stamp").Select(c => c.Value).SingleOrDefault();
if (securityStamp == null)
return true;
var dbSecurityStamp = await _userManager.GetSecurityStampAsync(user.Id);
return dbSecurityStamp == securityStamp;
}
public async Task<List<Claim>> GetClaimsFromUserAsync(Usuario user)
{
var claims = new List<Claim>
{
new Claim(JwtClaimTypes.Subject, user.Id),
new Claim(JwtClaimTypes.Name, user.UserName),
new Claim(JwtClaimTypes.PreferredUserName, user.UserName),
new Claim(ClaimTypes.Name, user.UserName) // to test
};
if (_userManager.SupportsUserEmail)
{
claims.AddRange(new[]
{
new Claim(JwtClaimTypes.Email, user.Email),
new Claim(JwtClaimTypes.EmailVerified, user.EmailConfirmed ? "true" : "false", ClaimValueTypes.Boolean)
});
}
if (_userManager.SupportsUserClaim)
{
claims.Add(new Claim(CustomClaimTypes.User.Name, user.Name));
... others claims
claims.AddRange(await _userManager.GetClaimsAsync(user.Id));
}
if (_userManager.SupportsUserRole)
{
var roles = await _userManager.GetRolesAsync(user.Id);
claims.AddRange(roles.Select(role => new Claim(JwtClaimTypes.Role, role)));
claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role))); // to test
}
return claims;
}
}
And, then Action Login Post:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model)
{
if (ModelState.IsValid)
{
var user = await _userManager.Users
.Include( ... the includes )
.SingleOrDefaultAsync(x => x.UserName
.Equals(model.Username, StringComparison.CurrentCultureIgnoreCase));
if (await _userManager.CheckPasswordAsync(user, model.Password))
{
AuthenticationProperties props = null;
if (AccountOptions.AllowRememberLogin)
{
props = new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration)
};
}
await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id, user.UserName));
await HttpContext.Authentication.SignInAsync(user.Id, user.UserName, props);
if (!_interaction.IsValidReturnUrl(model.ReturnUrl) || !Url.IsLocalUrl(model.ReturnUrl))
return Redirect(model.ReturnUrl);
return Redirect(Urls.Apps);
}
await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials"));
ModelState.AddModelError("", AccountOptions.InvalidCredentialsErrorMessage);
}
return View(new LoginInputModel(model.Username, model.ReturnUrl));
}
Nothing work!
What could be wrong?
You need to add the following to your authentication middleware.
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = JwtClaimTypes.Name;
}

Categories

Resources