I need to retrieve the roles associated to user, but I am working with wildfly, I have installed all jar keycloak in wildfly and my Java project, but can I retrieve this list by Java adapter?
Other options is call the rest api like any api by get, post, put, etc. But my first options was Adapters.
I make the authentication by adapters, but I do not find any way to retrieve roles, clients, realms, etc.
I am wrong or the adapter is just to authentications?
Anyone have a good example?
Set the option use-resource-role-mappings : true in keycloak.json
and you should be able to get roles in servlet as follows
KeycloakPrincipal principal = (KeycloakPrincipal)request.getUserPrincipal();
principal.getKeycloakSecurityContext().getToken().getResourceAccess("testclient").getRoles();
You can also get KeycloakPrincipal from context like this
Subject subject = (Subject) PolicyContext.getContext("javax.security.auth.Subject.container");
Set<KeycloakPrincipal> principals = subject.getPrincipals(KeycloakPrincipal.class);
and then get the roles
Thanks, here other way: (retrieve one role by name)
Keycloak keycloak = Keycloak.getInstance("http://localhost/auth", "realm-name", "client-name", authorization);
RoleRepresentation role = keycloak.realm("realm-name").clients().get(idOfClient).roles().get(roleName).toRepresentation();
To listing all user:
UsersResource users = keycloak.realm("realm-name").users();
And "authorization" is the string token bearer
"getInstance" have other methods to send for example pass and user.
If anyone else is still struggling, here's a complete answer:
Create a security context producer:
#RequestScoped
public class SecurityContextProducer {
#Inject
private HttpServletRequest request;
#Produces
public KeycloakSecurityContext getSecurityContext() {
return ((KeycloakPrincipal) request.getUserPrincipal())
.getKeycloakSecurityContext();
}
}
Use it like this:
#Inject
private KeycloakSecurityContext keycloakSecurityContext;
public List<String> getRolesKeycloak() {
Set<String> roleNames = keycloakSecurityContext.getToken().getRealmAccess().getRoles();
List<String> targetList = new ArrayList<>(roleNames);
return targetList;
}
It's not exactly the topic but I needed to find the roles associated with a specific user and this question pops first with my keywords web search. Here's what worked for me with keycloak client 13.0.1
RealmResource realmResource = keycloak.realm(REALM);
UsersResource usersResource = realmResource.users();
UserResource userResource = usersResource.get(USER_ID);
RoleMappingResource roleMappingResource = userResource.roles();
// either realmLevel or clientLevel
RoleScopeResource roleScopeResource = roleMappingResource.realmLevel();
List<RoleRepresentation> rolesRepresentation = roleScopeResource.listAll();
I didn't find it elsewhere, I hope it can be useful.
Related
I am trying to implement SSO in our app using keycloak-spring-security-adapter. The logging itself is working fine, but inside the app we have modules availability based on user roles/groups and i am not able to get user roles from SecurityContext to show users only what they should see.
SecurityContext context = SecurityContextHolder.getContext();
if(context.getAuthentication() != null) {
KeycloakPrincipal principal = (KeycloakPrincipal) context.getAuthentication().getPrincipal();
KeycloakSecurityContext session = principal.getKeycloakSecurityContext();
AccessToken accessToken = session.getToken();
AccessToken.Access realmAccess = accessToken.getRealmAccess();
logger.info("KEYCLOAK ROLES: " + realmAccess.getRoles());
above logger for my user always gives this:
KEYCLOAK ROLES: [offline_access, uma_authorization]
And these are not the roles registered in keycloak server, because the one used for authenticating my user is:
GSAP_APPLICATION_SUPPORT
I am not able to log into the app with user that is not a member of any keycloak-registered groups so thats why i know this process works fine.
Is there a way of getting list of current user roles from keycloak based on userId/token?
Hardcoding the roles checking inside the service is not a best practice, it's a common approach to divide role based functionalities by API like:
api/v1/admin/**, api/v1/user/**
Using this you can restrict the access to API by roles:
http.authorizeExchange()
.pathMatchers("your_endpoint").hasAnyRole("desired_role");
PS Please pay attention that keycloak adds the "ROLE_" prefix to the rolename, so you can
use ROLE_admin, ROLE_user in your configuration
or
use role names without "ROLE_" prefix (admin, user), and implement the JWT auth converter(example for Reactive (webFlux), you can do similar for Tomcat):
:
Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> getJwtAuthenticationConverter() {var converter = new ReactiveJwtAuthenticationConverter();
converter.setJwtGrantedAuthoritiesConverter(jwt -> {
Map<String, Object> realmAccess = jwt.getClaim("realm_access");
Collection<String> roles = (Collection<String>) realmAccess.get("roles");
return Flux.fromIterable(roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.toList());
});
return converter;
}
I've been following this tutorial in order to create an Authentication Server, but I'm facing some problems regarding the concepts, I guess.
Look, when I register a Client in Repository, I have to define some parameters, like its id, secret, authentication method, grant types, redirection uris and scopes:
#Bean
public RegisteredClientRepository registeredClientRepository() {
RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("articles-client")
.clientSecret("{noop}secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.redirectUri("http://127.0.0.1:8080/login/oauth2/code/articles-client-oidc")
.redirectUri("http://127.0.0.1:8080/authorized")
.scope(OidcScopes.OPENID)
.scope("articles.read")
.build();
return new InMemoryRegisteredClientRepository(registeredClient);
}
When I'm back to my Resource Server, I find that my client was successfully logged in and it returns with an "articles.read" scope. Everything is fine here, supposing that I want to protect my endpoints with the Client's scope, but this is not my case.
In my situation, I want to protect my endpoints according to my User's role in database.
I'll give you an example, so you don't have to read the whole Baeldung's website:
I try to access: http://localhost:8080/articles.
It redirects to: http://auth-server:9000, where a Spring Security Login Form appears.
When you submit the proper credentials (which are compared from a database using the default Spring Security schema), it basically gets you back to: http://localhost:8080/articles.
Well, in that point, I have an Authorization Token with the Client scope, but not the logged User role.
Is there an standard way to configure my project to achieve this or, do I have to think of a creative way to do so?
Thank you in advance.
For role based authentication you should map authorities in Oauth token.
OAuth2AuthenticationToken.getAuthorities() is used for authorizing requests, such as in hasRole('USER') or hasRole('ADMIN').
For this you need to implement the userAuthoritiesMapper, something like this:
#Configuration
public class AppConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.oauth2Login().userInfoEndpoint().userAuthoritiesMapper(this.userAuthoritiesMapper());
//.oidcUserService(this.oidcUserService());
super.configure(http);
}
private GrantedAuthoritiesMapper userAuthoritiesMapper() {
return (authorities) -> {
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
authorities.forEach(authority -> {
if (OidcUserAuthority.class.isInstance(authority)) {
OidcUserAuthority oidcUserAuthority = (OidcUserAuthority)authority;
OidcUserInfo userInfo = oidcUserAuthority.getUserInfo();
if (userInfo.containsClaim("role")){
String roleName = "ROLE_" + userInfo.getClaimAsString("role");
mappedAuthorities.add(new SimpleGrantedAuthority(roleName));
}
} else if (OAuth2UserAuthority.class.isInstance(authority)) {
OAuth2UserAuthority oauth2UserAuthority = (OAuth2UserAuthority)authority;
Map<String, Object> userAttributes = oauth2UserAuthority.getAttributes();
if (userAttributes.containsKey("role")){
String roleName = "ROLE_" + (String)userAttributes.get("role");
mappedAuthorities.add(new SimpleGrantedAuthority(roleName));
}
}
});
return mappedAuthorities;
};
}
}
I just created a project using latest spring social and spring social Facebook (and spring boot).
I'm trying to implement a Facebook signup which works fine except that it returns the same connection/profile for the first connected user every time.
My Facebook Profile Controller is:
#RestController
#RequestMapping(path = "/api/unsecure/facebook")
public class FacebookProfileController implements Serializable {
private static final long serialVersionUID = 1895700328147293496L;
#Autowired
private Facebook facebook;
#RequestMapping(path = "/profile", method = RequestMethod.GET)
public Result<FBUserVO> getUserFBProfile() {
if (!facebook.isAuthorized()) {
return ResultFactory.getFailResult("Facebook signup failed!");
}
FBUserVO user = FBUserToFBUserVO.INSTANCE.apply(facebook.userOperations().getUserProfile());
PagedList<Reference> friends = facebook.friendOperations().getFriends();
if (!friends.isEmpty()) {
user.setFriendsMessage("You have " + friends.size() + " friends playing. Join Now!");
}
return ResultFactory.getSuccessResult(user);
}
}
This works the first time... after that it will return the same profile info every time even though when I'm debugging the FacebookTemplate class, this is created using a new/valid access_token for a new user but for some reason the URLs are created maybe using the old token. I tried debugging the urls but they don't seem to send any access token.
Is there anything I'm doing wrong here? Can I get the current connection in another way?
You must use the ConnectionRepository class (which allows to remove the existing connection to Facebook provider for the first logged user which cause your problem).
Use a constructor like this and eliminate the Facebook autowiring:
private ConnectionRepository connectionRepository;
#Inject
public FacebookProfileController(ConnectionRepository connectionRepository) {
this.connectionRepository = connectionRepository;
}
through the connectionRepository instance you can get the Facebook object for the current logged user by doing:
connectionRepository.findPrimaryConnection(Facebook.class).getApi()
Note: To check if a user is not authenticated, you need to use:
connectionRepository.findPrimaryConnection(Facebook.class)==null
When you finish to extract data for the current logged user, you have to use the instruction below in order to delete the persistent connection created through constructor:
connectionRepository.removeConnections("facebook");
Hope this helps!
I have a web application deployed on Tomcat, which uses Tomcat's form authentication. When writing a new servlet, this allows me to find a request's user via HttpServletRequest#getUserPrincipal.
I would like to use Restlet in this app, and I was able to do so using Restlet's ServerServlet adaptor. However, it looks like I no longer have access to the user principal when receiving a new request in my resource classes. That is, the user prinicpal information is not carried through from Tomcat to Restlet.
Is there any way of obtaining the principal?
You should use the user principal with Restlet. As a matter of fact, Restlet has its own mechanism regarding security based on the challenge response. This allows to authenticate the user for a request, get its roles and set within ClientInfo#user. The servlet extension must be seen as an adapter to embed a Restlet engine within a servlet container but you shouldn't rely on the servlet API.
Here is the way to use security with Restlet:
public class MyApplication extends Application {
public Restlet createInboundRoot() {
Router router = new Router(getContext());
(...)
ChallengeAuthenticator ca = new ChallengeAuthenticator(getContext(),
ChallengeScheme.HTTP_BASIC, "admin");
Verifier verifier = (...)
Enroler enroler = new MyEnroler(this);
ca.setNext(router);
return ca;
}
}
Here is a sample implementation of Verifier:
public class MyVerifier extends SecretVerifier {
#Override
public boolean verify(String identifier, char[] secret) {
System.out.println(identifier);
System.out.println(secret);
//TODO compare with the Database
return true;
}
}
Here is a sample implementation of Enroler:
public class MyEnroler implements Enroler {
private Application application;
public MyEnroler(Application application) {
this.application = application;
}
public void enrole(ClientInfo clientInfo) {
Role role = new Role(application, "roleId",
"Role name");
clientInfo.getRoles().add(role);
}
}
You can then have access the security / authentication hints from the request within filter, server resource, ..., as described below:
User user = getRequest().getClientInfo().getUser();
List<Role> roles = getRequest().getClientInfo().getRoles();
You can notice this mechanism is opened in Restlet and can support a wide set of authentication (oauth2, ...). It's not really the good approach to use cookie-based authentication with REST. That said, you can use it even with Restlet.
Hope it helps you,
Thierry
I'm new to JAX-WS and there's a thing which I don't understand.
There's a ton of tutorials available on how to set up JAX-WS security, but in pretty much all cases BindingProvider.USERNAME_PROPERTY and BindingProvider.PASSWORD_PROPERTY are stored in some .xml file(depending on the container I believe) - they are "hardcoded" that is. And that's what I don't get. How can I authenticate a web service client by comparing BindingProvider.USERNAME_PROPERTY and BindingProvider.PASSWORD_PROPERTY with a user name and password that's in a database? I tried setting BindingProvider.USERNAME_PROPERTY and BindingProvider.PASSWORD_PROPERTY on the client side like this:
ShopingCartService scs = new ShopingCartService(wsdlURL, name);
ShopingCart sc = scs.getShopingCartPort();
Map<String, Object> requestContext = ((BindingProvider)sc).getRequestContext();
requestContext.put(BindingProvider.USERNAME_PROPERTY, userName);
requestContext.put(BindingProvider.PASSWORD_PROPERTY, password);
sc.someFunctionCall();
And then, on the server side retrieving like this:
#Resource
WebServiceContext wsContext;
#WebMethod
public void someFunctionCall() {
MessageContext mc = wsContext.getMessageContext();
mc.get(BindingProvider.USERNAME_PROPERTY);
mc.get(BindingProvider.PASSWORD_PROPERTY);
}
But I always get null, I didn't set up anything in xml, web service works just fine, except I can't get those variables :(
I'm running both on java 1.6, tomcat 6 and JAX-WS.
Any help with authenticating users with passwords from a database is greatly appreciated,
Thanks.
I think you are looking for JAX-WS authentication in application level, not HTTP basic in server level. See following complete example :
Application Authentication with JAX-WS
On the web service client site, just put your “username” and “password” into request header.
Map<String, Object> req_ctx = ((BindingProvider)port).getRequestContext();
req_ctx.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, WS_URL);
Map<String, List<String>> headers = new HashMap<String, List<String>>();
headers.put("Username", Collections.singletonList("someUser"));
headers.put("Password", Collections.singletonList("somePass"));
req_ctx.put(MessageContext.HTTP_REQUEST_HEADERS, headers);
On the web service server site, get the request header parameters via WebServiceContext.
#Resource
WebServiceContext wsctx;
#WebMethod
public String method() {
MessageContext mctx = wsctx.getMessageContext();
Map http_headers = (Map) mctx.get(MessageContext.HTTP_REQUEST_HEADERS);
List userList = (List) http_headers.get("Username");
List passList = (List) http_headers.get("Password");
//...
BindingProvider.USERNAME_PROPERTY and BindingProvider.PASSWORD_PROPERTY are matching HTTP Basic Authentication mechanism that enable authentication process at the HTTP level and not at the application nor servlet level.
Basically, only the HTTP server will know the username and the password (and eventually application according to HTTP/application server specification, such with Apache/PHP).
With Tomcat/Java, add a login config BASIC in your web.xml and appropriate security-constraint/security-roles (roles that will be later associated to users/groups of real users).
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>YourRealm</realm-name>
</login-config>
Then, connect the realm at the HTTP server (or application server) level with the appropriate user repository. For tomcat you may look at JAASRealm, JDBCRealm or DataSourceRealm that may suit your needs.
http://tomcat.apache.org/tomcat-6.0-doc/realm-howto.html
I had the same problem, and found the solution here :
http://www.mastertheboss.com/web-interfaces/336-jax-ws-basic-authentication.html?start=1
good luck
For an example using both, authentication on application level and HTTP Basic Authentication see one of my previous posts.
I was face-off a similar situation, I need to provide to my WS: Username, Password and WSS Password Type.
I was initially using the "Http Basic Auth" (as #ahoge), I tried to use the #Philipp-Dev 's ref. too. I didn't get a success solution.
After a little deep search at google, I found this post:
https://stackoverflow.com/a/3117841/1223901
And there was my problem solution
I hope this can help to anyone else, like helps to me.
Rgds,
iVieL
In your client SOAP handler you need to set javax.xml.ws.security.auth.username and javax.xml.ws.security.auth.password property as follow:
public class ClientHandler implements SOAPHandler<SOAPMessageContext>{
public boolean handleMessage(final SOAPMessageContext soapMessageContext)
{
final Boolean outInd = (Boolean)soapMessageContext.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outInd.booleanValue())
{
try
{
soapMessageContext.put("javax.xml.ws.security.auth.username", <ClientUserName>);
soapMessageContext.put("javax.xml.ws.security.auth.password", <ClientPassword>);
}
catch (Exception e)
{
e.printStackTrace();
return false;
}
}
return true;
}
}
If you put the username and password at clientside into the request this way:
URL url = new URL("http://localhost:8080/myapplication?wsdl");
MyWebService webservice = new MyWebServiceImplService(url).getMyWebServiceImplPort();
Map<String, Object> requestContext = ((BindingProvider) webservice).getRequestContext();
requestContext.put(BindingProvider.USERNAME_PROPERTY, "myusername");
requestContext.put(BindingProvider.PASSWORD_PROPERTY, "mypassword");
and call your webservice
String response = webservice.someMethodAtMyWebservice("test");
Then you can read the Basic Authentication string like this at the server side (you have to add some checks and do some exceptionhandling):
#Resource
WebServiceContext webserviceContext;
public void someMethodAtMyWebservice(String parameter) {
MessageContext messageContext = webserviceContext.getMessageContext();
Map<String, ?> httpRequestHeaders = (Map<String, ?>) messageContext.get(MessageContext.HTTP_REQUEST_HEADERS);
List<?> authorizationList = (List<?>) httpRequestHeaders.get("Authorization");
if (authorizationList != null && !authorizationList.isEmpty()) {
String basicString = (String) authorizationList.get(0);
String encodedBasicString = basicString.substring("Basic ".length());
String decoded = new String(Base64.getDecoder().decode(encodedBasicString), StandardCharsets.UTF_8);
String[] splitter = decoded.split(":");
String usernameFromBasicAuth = splitter[0];
String passwordFromBasicAuth = splitter[1];
}