Spring oauth2 retains user credentials after authorization_Code - spring-security-oauth2

In our application we have below flow:
First request is of grant_type authorization_code.
On success of above request user is redirected to login page to enter credentials.
New token is generated and User is logged in.
Now, if we keep the server up and repeat the above steps,then user is not redirected to login page. Instead credentials entered in above iteration are taken and new token is generated. It seems user data is being retained by spring.
Can someone pls help to identify the root cause? Do we have to invalidate the session manually? how?
Regards,
Smita

That is default behavior it will keep the user authenticated.
But if you want to change that. You add this in your Authorization Server Config
#Autowired
public void configureAuthorizationEndpoint(AuthorizationEndpoint authorizationEndpoint) {
DefaultRedirectResolver redirectResolver = new DefaultRedirectResolver() {
#Override
public String resolveRedirect(String requestedRedirect, ClientDetails client) {
SecurityContextHolder.clearContext();
return super.resolveRedirect(requestedRedirect, client);
}
};
redirectResolver.setMatchPorts(false);
authorizationEndpoint.setRedirectResolver(redirectResolver);
}
It will basically reset the authentication right before it sends the authorization_code to the relying party. So if you add this, the user will always be redirect to login page for each request.

Related

Using OAuth2 in Blazor and redirect user based on claims to certain page after successfull login

I use keycloak (OAuth2/Open ID Connect) to authorize and authenticate users in a Blazor Server Side application. Furthermore I use the services.AddAuthentication().AddOpenIdConnect() extension from Microsoft.
After successfull login I want to redirect the user to a certain page based on its claims. So f.e. if the user has the claim "gis" he should be redirected to "/gis" or if he has the claim "claim123" he should be redirected to page "/page456".
Right now I solve this problem by overriding OnInitialized in the MainLayout.razor and look if the user has a certain claim and then redirect him with the help of the NavigationManager.
#inject NavigationManager navMan;
//...
protected override void OnInitialized()
{
if (_user.HasClaim(t => t.Type == "claim123"))
{
navMan.NavigateTo("/page456");
}
}
The problem with this approach is, that it will first load the default index home page ( "/" ) and then after a short period of time will reload to the new destination. This "flickering" is not very nice and I thought there must be a better way to directly redirect after successful "/openid-connect" redirection. Can I may use some kind of middleware or one of the OpenIdConnectEvents?

IdentityServer 3 refresh user with refresh token

We are trying to set up Identity Server 3 in the right way.
We got authentication working fine and we manage to retrieve the refresh token.
The client application is using Angular.
Now when the acces_token expires any calls to the rest api fails (we managed to get it to return 401) but we are wondering how to re-authenticate the user.
In our tests, any api call made from Javascript is failing (401) but as soon as the page is refreshed the whole mechanism is kicking in. We do see that we are redirected to the identity server but it does not show up the login page, we are sent back to the client application with new tokens apparently.
What I would like to do is to refresh the access token without having to force the user to refresh the page.
What I'm not sure though is whose responsibility is it? Is that the client application (website) or the angular application? In other word, should the application handle this transparently for Angular or should angular do something when it receives a 401, in which case, I'm not too sure how the information will flow back to the web app.
Any clue?
Additional Information: We are using OpenId Connect
I got it working!
As I said in the comments I used this article. The writer is referencing a very nice lib that I am using as well.
Facts:
Identity Server 3 is requesting the client secret upon access token refresh
One should not store the refresh_token or the client_secret on the javascript application as they are considered unsafe (see the article)
So I chose to send the refresh_token as en encrypted cookie sith this class (found of ST BTW, just can't find the link anymore, sorry...)
public static class StringEncryptor
{
public static string Encrypt(string plaintextValue)
{
var plaintextBytes = plaintextValue.Select(c => (byte) c).ToArray();
var encryptedBytes = MachineKey.Protect(plaintextBytes);
return Convert.ToBase64String(encryptedBytes);
}
public static string Decrypt(string encryptedValue)
{
try
{
var encryptedBytes = Convert.FromBase64String(encryptedValue);
var decryptedBytes = MachineKey.Unprotect(encryptedBytes);
return new string(decryptedBytes.Select(b => (char)b).ToArray());
}
catch
{
return null;
}
}
}
The javascript application is getting the value from the cookie. It then deletes the cookie to avoid that thing to be sent over and over again, it is pointless.
When the access_token becomes invalid, I send an http request to the application server with the encrypted refresh_token. That is an anonymous call.
The server contacts the identity server and gets a new access_token that is sent back to Javascript. The awesome library queued all other requests so when I'm back with my new token, I can tell it to continue with authService.loginConfirmed();.
The refresh is actually pretty easy as all you have to do is to use the TokenClient from IdentityServer3. Full method code:
[HttpPost]
[AllowAnonymous]
public async Task<JsonResult> RefreshToken(string refreshToken)
{
var tokenClient = new TokenClient(IdentityServerConstants.IdentityServerUrl + "/connect/token", "my-application-id", "my-application-secret");
var response = await tokenClient.RequestRefreshTokenAsync(StringEncryptor.Decrypt(refreshToken));
return Json(new {response.AccessToken});
}
Comments are welcome, this is probably the best way to do that.
For future reference - using refresh tokens in an angular (or other JS) application is not the correct way as a refresh token is too sensitive to store in the browser. You should use silent renew based on the identityserver cookie to get a new access token. Also see the oidc-client-js javascript library, as this can manage silent renew for you.

How does http security hasRole() method work

I have developed a RESTful API in Spring which retrieves user info from a database (MongoDB) and returns that data to a client that consumes this REST API. But before the data is retrieved the REST API checks to see if the user is an admin or not. Only the admin can perform GET operations to retrieve user info
So in my securityconfig.java class I have this:
http.authorizeRequests()
.antMatchers("/users/get/**", "/users/get/**").hasRole("ADMIN");
http.httpBasic();
Now, the users all have their roles and everything is working as expected in the sense that when I call curl adminusername:adminpassword#localhost:8080/users/get/AllUsers
I am able to retrieve all the users because the user with the username of adminusername is an admin and he has permission. But if I replace adminusername with a non-admin username, I get an access denied error.
My question is, is the adminusername:adminpassword part before the #localhost:8080.... the header of the HTTP request?
The reason I ask is because I need to create a client that is able to log in, have his credentials (username and password) verified, and have the username and password used as the session id, so that any time the client makes HTTP request calls after being logged in, the username and password is appended to the header and is processed by the REST API.
The hasRole() of the
http.authorizeRequests()
.antMatchers("/users/get/**", "/users/get/**").hasRole("ADMIN");
in the security config is dependent on the username:password before the #localhost:8080.... is this the same thing as #RequestHeader of the spring rest API?
First of all, Http Basic is a way, to send user credentials through header -
var header = {'Authorization': 'Basic '+btoa(username+':'+password)} //javascript
Second thing are authorities for a user. You in your system have to create user, and add them priviliges, for example:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("mati").password("qwerty").roles("ADMIN", "USER").and()
.withUser("mati2").password("qwerty").roles("USER");
}
These users became later objects from class Principal which has their granted Authorities (like "ROLE_USER" or "ROLE_ADMIN").
When you are calling for example
curl adminusername:adminpassword#localhost:8080/users/get/AllUsers
Spring Security will check for proper Principal (with given username and will check if password matches), load it to the SecurityContext and will check if Principal has authority "ROLE_ADMIN" (by hasRole("ADMIN")). I must add that this was tricky, and I don't remember exactly, if you it washasRole("ADMIN") or hasRole("ROLE_ADMIN").
I hope this will answer your question(s).

Manually associate session with current request asp.net MVC

I have a MVC 5 asp.net website where I need to expose a number of REST APIs to a stand-alone mobile client. The rest of the site is using Forms based security where it sets the ASP.NET_SessionId as a cookie, and that is used to authenticate the user with the request after they log in. With my mobile application, I am not able to use the cookie method because of the cross-doman issue. What I would like to do is add a header "X-SessionId" with the value of the ASP.NET_SessionId, then on the server side, have a filter that looks for that field, and if it is present, associates the request with the given session. (Client will log in with an AJAX POST call which will return the ASP.NET_SessionId upon successful login).
Is this possible?
Something like this?
public sealed class CustomSecurityAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException("filterContext");
if (string.IsNullOrEmpty(filterContext.HttpContext.Request.Headers["X-SessionId"]) && IsAuthenticated(ilterContext.HttpContext.Request.Headers["X-SessionId"]))
filterContext.Result = new HttpNotFoundResult();
}
private bool IsAuthenticated(string sessionId)
{
// get your user details from your database (or whatever)
var user = new UserRepository().Get(sessionId);
if (user == null)
return false;
// build up an identity, use your own or out of the box.
FormsIdentity itentity = new MyIdentity(user);
// Set the user
filterContext.HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(itentity , user.Roles);
return true;
}
}
You are going to have to store current sessions in your database, so for example when a user logs in grab the sessionid and stick it in the db, so you know they have 1..n current sessions.
Then you can look it up as part of your authentication.
Edit:
Let's take a step back, never mind cookies and sessions for the moment.
You have a website and a restful api, they both servce different purposes and clients and have different security requirements.
So what are the most common options for securing your Api?
Basic authentication.
Most restful APIs require a username/password to be sent through with each request, as part of the headers or in the request itself.
An authentication token
You can provide a token associated with a user account (a guid could suffice) when requests are made you check for the token.
Using an existing protocal like OAuth
I would recommend using these common scenarios to be sure you don't miss something and open your self up to security vulnerabilities.
Is there a reason you can't use any of these?

Forms Authentication and POST requests from AJAX

We have an ASP.NET app protected by forms authentication. The app uses MS AJAX heavily to call its web-services.
When the forms authentication times out, and a GET-request happens - all is fine (the user is redirected to a login page).
BUT when the forms authentication times out and a POST-request happens (ajax) - no redirect happens, instead the app returns "401 unathorized" and the browser prompts for username and password (not a login form, but a browsers built-in dialog). Of course entering ANY username/password never helps.
How do I handle this?
UPDATE: After looking with firebug, I just found out that regular POST requests redirect to login fine, it's only web-service calls that throw "401 Unauthorizes".
The difference between a regular request and web-service is URL. Which is "page.aspx" for a regular post-request and "service.asmx/MethodName" for webservices...
Ok, answering my own questin.
After looking into this issue and researching a bit more I found that when a web-app is protected by Forms-Authentication and the user is not authenticated, this is what happens:
If it's a GET-request - the user is
redirected to the login page.
If it's a POST-request to a page - the user is
redirected to the login page.
If it's a POST-request to a web-service - the
user gets 401-unauthorized
Thats how ASP.NET works
And if a web-service is called by AJAX (xmlHttpRequest object) and returns 401 - of course the browser shows a pop-up login box.
Now, what should you do is add some code to Application_PostAuthenticateRequest that will prevent throwing 401 for webservices.
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
if (Request.RequestType == "POST" //if its POST
&& !User.Identity.IsAuthenticated //if user NOT authed
&& !HasAnonymousAccess(Context) //if it's not the login page
)
{
//lets get the auth type
Configuration config = WebConfigurationManager.OpenWebConfiguration("~");
SystemWebSectionGroup grp = (SystemWebSectionGroup)config.GetSectionGroup("system.web");
AuthenticationSection auth = grp.Authentication;
//if it FORMS auth
if(auth.Mode== AuthenticationMode.Forms)
{
//then redirect... this redirect won't work for AJAX cause xmlHttpRequest can't handle redirects, but anyway...
Response.Redirect(FormsAuthentication.LoginUrl, true);
Response.End();
}
}
}
public static bool HasAnonymousAccess(HttpContext context)
{
return UrlAuthorizationModule.CheckUrlAccessForPrincipal(
context.Request.Path,
new GenericPrincipal(new GenericIdentity(string.Empty), null),
context.Request.HttpMethod);
}
I see two solutions:
(1) "Heart beat" mechanism. On each page include a script that will "ping" the server by some dummy ajax request, like:
<script>
setInterval(ping, 60000); // based on comment by John
function ping()
{
$.get('/do/nothing');
}
</script>
This way the session shouldn't expire as long as the browser window is open.
(2) On each ajax request check the status of the response. If the response has "401 unauthorized" code (or any other code different that 200), that means that the session expired and instead of loading the response into some dialog box in the page redirect the user to login page.
Conclusion based on comments:
The best solution would be to combine the two above mechanisms. Heartbeat mechanism will help to keep the session alive as long as the page is displayed in the browser. But in doesn't guarantee that for sure. The connection to the server can be broke and reopened when the session is expired. So you should check the response status anyway.

Resources