I followed this example to get the implicit flow working.
http://weblogs.thinktecture.com/cweyer/2012/11/oauth2-in-thinktecture-identityserver-v2-implicit-grant-flow-with-javascript.html
My question is, how would I signout a user? I'm hoping someone out there knows, because I haven't been able to find any examples. Right now I can trigger the authorize window and process it all correctly and use the access token for my requests, but I don't know how to make it so I can switch users.
To logout you have to create custom controller with logout method.
public void Logout()
{
// You should be able to revoke thinktecture token like this. (haven't tested this out)
var sam = FederatedAuthentication.SessionAuthenticationModule;
sam.SignOut();
// Or you should be able to logoff like this when using a membership provider. (this way works for me)
//_yourMembership.Logout();
Thread.CurrentPrincipal = null;
HttpContext.Current.User = null;
}
Here is a sample of token revocation:
https://github.com/brockallen/BrockAllen.MembershipReboot/blob/master/src/BrockAllen.MembershipReboot/Authentication/SamAuthenticationService.cs
Related
Question: How can I enforce existing users to set up 2FA in .Net Core 3.1 Identity?
I have seen a couple of answers here already, but I have issues with them as follows:
Redirect user to set up 2FA page on login if they do not have it set up. Problem with this is that the user can simply jump to a different url to avoid this, therefore it is not actually enforced.
Have some on executing filter that checks if the user has 2FA enbaled or not and if not redirect them to MFA set up page. The issue I have with this is that on every single navigation the server must go to the database to check whether the user has this field enabled, thus creating a significant performance hit on each request. I know one trip to the database may not sound like much but I have worked with applications where this was the norm and other things used this method, causing a pile up of pre action db queries. I want to avoid this kind of behavior unless absolutely necessary.
My current idea is to on login:
Check the users credentials but NOT log them in
userManager.CheckPasswordAsync(....)
If the credentials pass, check if the user has 2FA enabled or not. If they do, continue through login flow, if not:
Generate a user token:
userManager.GenerateUserTokenAsync(.......)
and store this along with the username in a server side cache. Then pass a key to the cached items with a redirect to the 2FA setup page, which will not have the [authorize] attribute set, allowing users not logged in to access it.
Before doing anything on the 2FA set up page, retrieve the cached items with the provied key andverify the token and username:
userManager.VerifyUserTokenAsync(......)
If this doesn't pass, return Unauthorized otherwise continue and get the current user from the supplied UserName in the url that was passed via a cache key. Also dump the cached items and key so that should the url be snatched by a dodgy browser extension it can't be used again.
Continue to pass a new cache key to new user tokens and usernames to each 2FA page to authenticate the user as they navigate.
Is this an appropriate use of user tokens? And is this approach secure enough? I'm concerned that having the user not logged in presents security issues, but I think it is necessary in order to avoid the previously mention problem of going to the database on every request to check 2FA, as with this method trying to navigate away will just redirect to login.
I implemented this via a Filter Method
I have a BasePageModel which all my pages inherit
public override async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
{
if (!User.Identity.IsAuthenticated)
{
await next.Invoke();
return;
}
var user = await UserManager.GetUserAsync(User);
var allowedPages = new List<string>
{
"Areas_Identity_Pages_Account_ConfirmEmail",
"Areas_Identity_Pages_Account_ConfirmEmailChange",
"Areas_Identity_Pages_Account_Logout",
"Areas_Identity_Pages_Account_Manage_EnableAuthenticator",
"Areas_Identity_Pages_Account_ResetPassword",
"Pages_AllowedPageX",
"Pages_AllowedPageY",
"Pages_Privacy"
};
var page = context.ActionDescriptor.PageTypeInfo.Name;
if (!user.TwoFactorEnabled && allowedPages.All(p => p != page))
{
context.Result = RedirectToPage("/Account/Manage/EnableAuthenticator", new { area = "Identity" });
}
else
{
await next.Invoke();
}
}
I then changed both the Disable2fa and ResetAuthenticator pages to redirect to the main 2fa page
public IActionResult OnGet() => RedirectToPage("./TwoFactorAuthentication");
And removed the reset/disable links from that page
I chose to implement a more modern and OAuth friendly solution (which is inline with .Net Core Identity).
Firstly, I created a custom claims principal factory that extends UserClaimsPrincipalFactory.
This allows us to add claims to the user when the runtime user object is built (I'm sorry I don't know the official name for this, but its the same thing as the User property you see on controllers).
In here I added a claim 'amr' (which is the standard name for authentication method as described in RFC 8176). That will either be set to pwd or mfa depending on whether they simply used a password or are set up with mfa.
Next, I added a custom authorize attribute that checks for this claim. If the claim is set to pwd, the authorization handler fails. This attribute is then set on all controllers that aren't to do with MFA, that way the user can still get in to set up MFA, but nothing else.
The only downside with this technique is the dev needs to remember to add that attribute to every non MFA controller, but aside from that, it works quite well as the claims are stored in the users' cookie (which isn't modifiable), so the performance hit is very small.
Hope this helps someone else, and this is what I read as a base for my solution:
https://damienbod.com/2019/12/16/force-asp-net-core-openid-connect-client-to-require-mfa/
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/mfa?view=aspnetcore-5.0#force-aspnet-core-openid-connect-client-to-require-mfa
I have never used identity server before and I was wondering how to receive the current user info if they have been authenticated using net core 3.
Using the barebones create react app -auth weather app template
in the WeatherForecastController we have the authorize attribute:
[Authorize]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
With the following endpoint:
[HttpGet]
public IEnumerable<WeatherForecast> Get()
Which will return the correct data if you have logged in and been granted a jwt bearer token.
I would like to create an endpoint that:
1. Allows anonymous access(logged in or not so the authorize attribute needs ditching or overriding)
2. It should then try to authenticate the user
3.If the user can be identified then the endpoint will return the correct data else if the user cannot be identified it will return a subset of the data
I'm struggling to retrieve the user who has made the request information, I believe i'm supposed to use GET /connect/userinfo identityserver endpoint with the bearer token as a parameter but I have no idea how to do this. I would like to access the user_id so I can corrispond with the identityUser table.
I have also tried:
var identity = (ClaimsIdentity)User.Identity;
IEnumerable<Claim> claims = identity.Claims;
It just feels like theres a proper way to do this stuff that i'm missing. I'd be grateful for someone to point me in the right direction.
It should then try to authenticate the user
Browsers communicate with web applications. You should have a web application to be able to redirect back to IDS4 to login. What you are have is an API. Later Web applications communicate with web APIs. Read more here
If the user can be identified then the endpoint will return the correct data else if the user cannot be identified it will return a subset of the data
What you are looking for is Authorization, you can manually do this check on the endpoint. You can access current user using ControllerBase.User ass a result of passing a valid JWT tokens to the API. Considering you already setup authentication using code like bellow:
services.AddAuthentication("Bearer").AddJwtBearer("Bearer",
options =>
{
options.Authority = "http://localhost:5000";
options.Audience = "api1";
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters()
{
NameClaimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"//To set Identity.Name
};
});
I'm struggling to retrieve the user who has made the request information
You can simply use ControllerBase.User to get the current authenticated user on API, no need to call any endpoint.
Update: Setting NameClaimType as posted on code above will set Identity.Name to the user's identifier.
I'm refactoring a pure PHP application to Laravel. I've already made Laravel auth. It works. However, I need a way of migrate old users and passwords to the new table. My idea is, at the login POST, verify if the user is stored on old table, if yes, insert that user on new table and procede with laravel auth. Can I override the login's method in LoginController to make these changes? Is It possible?
I had to override the login methods, adding the function migrationUserModelInternet($request).
protected function login(Request $request) {
$this->validateLogin($request);
$this->migrationUserModelInternet($request);
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if ($this->attemptLogin($request)) {
return $this->sendLoginResponse($request);
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
I had implemented OpenID Connect server that generates access tokens for mobile client based on username/password using OpenIddict.
My next goal was to provide ability to generate Access Token using 3-rd party tokens (social login for example), and I started from integration with Google token, but stuck as cannot find any samples/informations about how to do this.
The only one idea that I currently have is to make request to "/connect/token" endpoint and send Google token in "code" parameter, for example in "google:" format, then override OpenIdConnectServerProvider.DeserializeAuthorizationCode method:
Called when receiving an authorization code. An application may use this context to deserialize the code using a custom format and to skip the default logic using
So I have created own CustomProvider class based on OpenIddictProvider, registered it
services.AddOpenIddict<ApplicationUser, ApplicationRole, ApplicationDbContext, int>()
.Configure(builder =>
{ builder.Provider = new CustomProvider(sp.GetRequiredService<SignInService>()); }
and overridden the DeserializeAuthorizationCode method:
public override async Task DeserializeAuthorizationCode(DeserializeAuthorizationCodeContext context)
{
string code = context.Request.Code;
if (code.StartsWith("google:"))
{
string token = code.Replace("google:", "");
var principal = new GoogleTokenValidator().ValidateToken(token, null).Result;
var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), "Bearer");
ticket.SetPresenters(context.Request.ClientId);
context.Ticket = ticket;
context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddDays(1);
context.HandleResponse();
await _signInService.Login(principal);
return;
}
else
{
base.DeserializeAuthorizationCode(context);
}
}
where GoogleTokenValidator is a custom class for Google token handling (it makes call to Google User Information Endpoint and generate ClaimsPrincipal), based on "copy-pasted" code from GoogleHandler class in aspnet/Security repo.
In general it is working with some additional hacks, but I have strong feeling that reinventing the wheel...
In general it is working with some additional hacks, but I have strong feeling that reinventing the wheel...
You're not only reinventing the wheel, but you're also implementing something totally non-standard that is not supported (at all) by OpenIddict.
Here's the approach I recommend (which is the one we use in the MVC server sample):
The OAuth2/OpenID Connect client application redirects the user agent to your authorization controller (you can take a look at this controller for an example).
OpenIddict will validate the authorization request and allow your controller to be invoked if it's fully valid.
If the user is not already logged in, your authorization controller will redirect the user to the login endpoint, provided by AccountController. At this point, you're free to propose local authentication (e.g using a username/password couple) or Google authentication (you can use the Google authentication middleware for that). You can even offer 2-FA as part of this login process.
Once the user is logged in (e.g after a registration process and/or an external authentication association), his/her browser is redirected back to the authorization endpoint, where a consent form indicating he/she's about to allow your JS app to access his personal data on his/her behalf is displayed.
When the user allows your client application to access his data, the request is handled by your authorization controller, that calls SignInAsync to inform OpenIddict that an authorization code/access token should be returned to your application.
We have a need to provide Mixed Mode Authentication and we've spent weeks chasing down samples, and bits and pieces and finally we have our ClaimsIdentity properly built, we just need to figure out a way to have OWIN recognize our ClaimsIdentity as the logged in user.
Everything we've found thus far is defining app.UseCookieAutentication in Startup.Auth.ConfigureAuth(). Set AuthenticationType = "Cookies", then set a login path.
However, we use a custom version of Identity Server 3 and in addition to UseCookieAuthentication, we also use app.UseOpenIdConnectAuthentication.
We've had issues with setting the UseCookieAuthentication LoginPath. We've tried putting something in it and leaving it empty.
Code we use to login our Windows users, we make calls to IdentityServer, get our tokens and claims.
We then use UserManager.CreateIdentityAsync as follows.
var _identity = await _userManager.CreateIdentityAsync(_slUser, DefaultAuthenticationTypes.ApplicationCookie);
Everything appears good here.
Then we use that Identity with the following code:
var _ctx = HttpContext.Current.GetOwinContext();
var _authenticationManager = _ctx.Authentication;
_authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = false }, _identity);
The code runs but when we check _ctx.Authentication.User, the identity we created still isn't showing up which results in a user getting redirected to the login page on the Identity Server.
So in summary, we're trying to figure out how to take a ClaimsIdentity we've authenticated and built up in code and have it become of apart of the OwinContexts Authentication.User.Identities so it will be available throughout our application.