I'd like to read the session cookie in the java quarkus app.
According to this page https://quarkus.io/guides/security-built-in-authentication#form-auth, it's possible so we can get access to the information across services.
I'd like to use it to manage all possible sessions of the same user, and add some information, specially for websocket.
Thanks!
I'm guessing you need to add Security Context to endpoints, like this :
#GET
#Path("/")
public String getSubjectSecured(#Context SecurityContext sec) {
Principal user = sec.getUserPrincipal();
// Do stuff with user
}
See documentation
Related
I'd like to know if and how it is possible to set and update Keycloak (OpenID-Connect) AccessToken or IdToken attributes (so called Claims) by a client web application, after successful authentication.
The use case is to add specific user-attributes (e.g. number of pets, hair color, favorite car, etc.) to the Access- or Id-Token, while the user is logged in to our web application based on a Vue.js Frontend and a SpringBoot Backend, mainly exposing REST Services to the Frontend.
A second web-application, also using the Keycloak Token for user authentication/authorization (Single-Sign-On feature) should be able to read the user-attributes added by the first web-application to the Token.
Even I'm afraid that adding and changing of Token payload is not allowed by architectural design of OpenId-Connect, I nevertheless hope it will be possible anyhow.
Token-attributes are implemented as 'Claims' in OpenId-Connect. And Keycloak supports 'Claim' mappings during the authentication process (set by static mappings on Keycloak server as well as by code that runs on the Keycloak server).
The appropriate methods to set and get Claim key-value pairs are mentioned by the following articles:
How to create a Script Mapper in Keycloak?:
token.getOtherClaims().put("myClaimName", "claim value");
Include user locale to the Keycloak ID token:
Map<String, Object> otherClaims = token.getOtherClaims();
if (otherClaims.containsKey("myClaimName")) {
String claimValue = String.valueOf(otherClaims.get("myClaimName"));
}
For the case changing of Token payload by Keycloak clients is not allowed by architectural design, I appreciate any suggestion on best practices to hand over dynamically added user-attributes from one webapp to another webapp, having the same Keycloak Access- and/or Id-Token in common.
Yes, changing of token payload by user application is not allowed/possible by architectural design. App doesn't own private key, which is required to create proper token signature, when you change payload.
I have a multi-tenant application (springboot keycloak adapter + spring security) secured by Keycloak. Given the multi-tenant nature of the project, I wrote a multi-client connector which works fine.
On the official Keycloak doc, it is recommended (for multi-tenant applications) to model each tenant as a new realm, but for me it works better to have multiple clients within the same same realm. This is due to following advantages:
Client scopes, groups and other configs can be shared
Users don't need to be duplicated on N different realms
SSO login works perfectly within same realm clients (by using bearer
services +CORS)
So, everything works fine except for 1 thing, my initial SSO access_token (which is then shared across all bearer-only services by means of CORS) is kind of big (it shows all the resources - tenants - and its roles within each resource/tenant).
I'd like to limit the size of the access_token, by means of using "scopes" to restrict the roles in the token to only those meaningful to the tenant where I'm logged in at that time. For this, I'm manually firing a Request to the auth server (outside of the standard functionality provided by springboot/spring security) with the goal of manually overwriting whatever access-token exists within my app, with the new one generated by my extra request.
My "new" token request looks similar to this:
SimpleKeycloakAccount currentUserAccount = (SimpleKeycloakAccount) auth.getDetails();
String authServerUrl = currentUserAccount.getKeycloakSecurityContext().getDeployment().getAuthServerBaseUrl();
String realm = currentUserAccount.getKeycloakSecurityContext().getDeployment().getRealm();
String resource = currentUserAccount.getKeycloakSecurityContext().getDeployment().getResourceName();
String refreshToken = currentUserAccount.getKeycloakSecurityContext().getRefreshToken();
String token = currentUserAccount.getKeycloakSecurityContext().getTokenString();
Http http = new Http( new Configuration(authServerUrl, realm, resource,
currentUserAccount.getKeycloakSecurityContext().getDeployment().getResourceCredentials()
, null),
(params, headers) -> {});
String url = authServerUrl + "/realms/" + realm + "/protocol/openid-connect/token";
AccessTokenResponse response = http.<AccessTokenResponse>post(url)
.authentication()
.client()
.form()
.param("grant_type", "refresh_token")
.param("refresh_token", refreshToken)
.param("client_id", resource)
.param("client_secret", "SOME_SECRET")
.param("scope", "SOME_SCOPE_TO_RESTRICT_ROLES")
.response()
.json(AccessTokenResponse.class)
.execute();
// :) - response.getToken() and response.getRefreshToken(), contain new successfully generated tokens
My question is, how can I force my-app to change/reset the standard access-token & refresh_token obtained by the usual means, with these "custom created" tokens? or is that possible at all?
Thx for any feedback!
Further Information
To clarify more, lets analyze the behavior of a typical springboot/spring security project integrated with Keycloak:
You protect your endpoints with "roles" via configurations (either on the application.properties, or on the SecurityContext)
You know that this Spring application talks in the back channel with the Keycloak authorization server, that's how you become the access_token (But all this is a black box for the developer, you only know a Principal was created, a Security Context, Credentials; etc - everything happens behind the curtains)
Considering those 2 points above, imagine that you use an Http library to basically request a new token towards the auth server token endpoint like in the code above (yes filtered by scopes and everything). So the situation now is that though you have created a valid access_token (and refresh_token); since they were created "manually" by firing a request towards the token endpoint, this new token hasn't been "incorporated" to the application because No new Principal has been created, no new security context has been generated, etc. In other words, to the springboot application this new token is non-existent.
What I'm trying to accomplish is to tell sprinboot/spring security: "Hey pal, I know you didn't generate this token yourself, but please accept it and behave as if you'd have created it".
I hope this clarifies the intent of my question.
You can revoke a token using org.springframework.security.oauth2.provider.token.ConsumerTokenServices#revokeToken method.
On the Autorization Server:
#Resource(name="tokenServices")
ConsumerTokenServices tokenServices;
#RequestMapping(method = RequestMethod.POST, value = "/tokens/revoke/{tokenId:.*}")
#ResponseBody
public String revokeToken(#PathVariable String tokenId) {
tokenServices.revokeToken(tokenId);
return tokenId;
}
Of course, you'll have to secure this method since is a very sensitive one.
In the case that each tenant is a separate client you can just use keycloak's "Scope" mapping at each client. Just turn off Full Scope Allowed and your tokens will only contain the user's roles for that specific client (tenant).
"Scope Mappings" is a a non intuitive way of saying "Define what roles should go into the access token" :-)
When turned off the UI changes and you even can configure what other roles of other clients should additionally go into the access token.
Just to give some closure to this question:
No, there doesn't seem to be any elegant or intended way to force a manual token renewal by means of using springboot/spring security keycloak connector.
The Javascript connector can do this trivially like this:
// for creating your keycloak connector
var keycloak = Keycloak({
url: 'http://localhost:8080/auth',
realm: '[YOUR_REALM]',
clientId: '[YOUR_CLIENT]'
});
// for login in (change scopes list to change access capabilities)
var options = {
scope: [EMPTY_STRING_SEPARATED_LIST_OF_SCOPES] // <-- here specify valid scopes
};
keycloak.login(options); // <-- receive a new token with correctly processed scopes
Given how easy it is to do this with the Keycloak client JS adapter, and how obscure it is to do this with the springboot/spring security adapter, it follows following:
Security design seems intended to have 2 (Keycloak security) layers; the first is a front-facing public client (usually password protected), and the 2nd layer is composed of several bearer-only services which would ussually only accept acces-tokens. If for those bearer-only services you want to implement finner grained control via scopes, you achieve that trivially by using a javascript based Keycloak client (other connectors as explained won't deal nicely with the header modification necessary to deal with OAuth2 scopes).
A HTML5 UI is connected to the backend (REST Jersey to business logic to Hibernate and DB). I need to create and maintain a session for each user login until the user logs out.
I am clueless on how to approach this problem.
I followed this approach
Initially when the User is successfully logs in , i am setting attribute under session as shown below
HttpSession session = request.getSession(true);
session.setAttribute("islogged", "islogged");
String value = (String)session.getAttribute("islogged");
System.out.println("****************** The User Logge in Value"+value);
Later in a different page i am checking if the user is logged in or not this way
public String checkIfUserLoggedIn() throws JSONException,ClassNotFoundException, SQLException
{
HttpSession session = request.getSession();
String value = (String)session.getAttribute("islogged");
if(value==null)
{
// always its coming here only
}
}
I agree with francesco foresti, please do not rely on HTTP session without Auth. this is unsafe, and quite dangerous for your app.
Have you been implementing a specific session mecanism ?
If not, jersey as it is will not store session data as it. Every call that you will make will give you a session id that is different from yours.
You have to make authentication & use the auth token in order to identify you session.
use JAX-RS
Please do use an auth mecanism as defined : https://jersey.java.net/documentation/latest/security.html
#Path("authentication")
#Singleton
public static class MyResource {
// Jersey will inject proxy of Security Context
#Context
SecurityContext securityContext;
#GET
public String getUserPrincipal() {
return securityContext.getUserPrincipal().getName();
}
}
or use another framework : Spring, Shiro.... etc.
I really prefer that solution, since another framework will implement a lot of stuff for you. You gain a lot of time doing so.
Please take a look to official jersey doc: https://jersey.java.net/documentation/latest/index.html
I wouldn't rely on the http session. My approach would be to put an "Authorization" field in the header of the response that the server returns when the user logs in, and ask the user to put the very same header in each suqsequent call. In this header you put the informations that help the server find the identity of the user
(take a look at what twitter does as an example : https://dev.twitter.com/oauth/overview/authorizing-requests). The server could save the informations about the logged in user in the database, or You could create a Map in a Singleton that would serve as the "authorization gatekeeper" for your services.
I have a system running on Tomcat, with HTML/JSP in front-end, and java/Spring/Struts in backend.
I made a login-feature where the user enters his username and password.
In backend, I validate the username and password to the stored user in DB.
If match, I store the username in HTTPsession:
session.setAttribute( "username", name );
Then, on every class-action in backend, I add the following code:
HttpSession session = request.getSession();
if(session.getAttribute("username") == null) {
return mapping.findForward("invalidUser");
}
the invalidUSer-mapping redirects the user back to the login-page.
How secure is this?
Is there a way to check the httpsession without adding my validation-code to every class?
Do you guys have tips (or examples/tutorials) on how to do this differently? The system is already created and in production, so I do not want to do too many architecural changes.
As you are already using Spring in your project, you may want to look into Spring Security to replace your bespoke security mechanisms. You can configure it to protect certain resources within your application, authenticate against bespoke database back-ends, LDAP directories, etc. This will allow you to remove all manual checking of the session to see if the user is authenticated, and will redirect anonymous users to the specified login page when they attempt to access protected resources.
Along with the spring security filter definition in web.xml, the configuration can be specified in a single spring-security.xml file (imported into your root app config) using the security:http namespace to define the login page, protected resources, logout page, security headers etc. You could use a org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl instance configured as a bean to define the user service which can be referenced by the authentication-provider - see the docs, its very flexible.
Hope that's useful.
I'm implementing a RESTful web service using ASP.Net Web Api. I have concluded to use Basic authentication + SSL to do the authentication part. What is the best/correct way to implement that?
My first attempt was to do it manually, parsing the Authorization header, decoding and verifying the user against my database. It works, but I wonder if I am missing something.
I've seen some solutions using user roles and principals. While I'm not sure what these actually do, I'm almost sure I will not be needing these, since in my database I define my own users and their roles.
Also what I haven't yet completely understand, is if the consumers of the service must sent the credentials with each request or they are somehow cached. Should my service do something in order for this to happen, or it's completely up to the consumer to handle this?
And a last question about clients making requests with javascript. Would there be any "cross domain request" problems if they try to use the service?
Jamie Kurtze provides a good explanation of using Basic Authentication here ASP.NET Web API REST Security Basics
From my understanding, if you want your requests to be stateless then each request will require the Authentication field to be set
Jamie Kurtze wraps the necessary code in a class derived from DelegateHandler, while Rick Strahl checks if the call is valid using a Filter. You can read more at his blog post on this topic at A WebAPI Basic Authentication Authorization Filter
Use basic authentication for the initial (sign in) request by adding a [BasicHttpAuthorize] attribute to the appropriate controllers/methods. Specify the Users and Roles with the attribute if desired. Define BasicHttpAuthorizeAttribute as a specialized AuthorizeAttribute like this:
public class BasicHttpAuthorizeAttribute : AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext actionContext)
{
if (Thread.CurrentPrincipal.Identity.Name.Length == 0) { // If an identity has not already been established by other means:
AuthenticationHeaderValue auth = actionContext.Request.Headers.Authorization;
if (string.Compare(auth.Scheme, "Basic", StringComparison.OrdinalIgnoreCase) == 0) {
string credentials = UTF8Encoding.UTF8.GetString(Convert.FromBase64String(auth.Parameter));
int separatorIndex = credentials.IndexOf(':');
if (separatorIndex >= 0) {
string userName = credentials.Substring(0, separatorIndex);
string password = credentials.Substring(separatorIndex + 1);
if (Membership.ValidateUser(userName, password))
Thread.CurrentPrincipal = actionContext.ControllerContext.RequestContext.Principal = new GenericPrincipal(new GenericIdentity(userName, "Basic"), System.Web.Security.Roles.Provider.GetRolesForUser(userName));
}
}
}
return base.IsAuthorized(actionContext);
}
}
Have the initial response include an API key for the user. Use the API key for subsequent calls. That way, the client's authentication remains valid even if the user changes username or password. However, when changing password, give the user an option to "disconnect clients", which you implement by deleting the API key on the server.
Have a look here for a good basic authentication implementation
http://leastprivilege.com/2013/04/22/web-api-security-basic-authentication-with-thinktecture-identitymodel-authenticationhandler/
there is more to read about it at:
https://github.com/thinktecture/Thinktecture.IdentityModel.45/wiki