Keycloak HTTP 401 Unauthorized while creating new user using spring boot service - spring-boot

Getting javax.ws.rs.NotAuthorizedException: HTTP 401 Unauthorized while trying to create new user in keycloak with spring boot service.
In KeycloakConfig class. it is breaking during creating keycloak instance with HTTP 401 Unauthorized. "message": "javax.ws.rs.NotAuthorizedException: HTTP 401 Unauthorized",
Am I missing anything? I dont have any extra role configuration in the keycloak client or realm role.
I was thinking to add ".authorization(...) in KeycloakBuilder.builder but I'm not sure what to specify.
public static Keycloak getInstance(User user) {
if (keycloak == null) {
keycloak = KeycloakBuilder.builder()
.serverUrl("http://localhost:8080/auth")
.realm("myrealm")
.username(user.getUsername()).password(user.getPassword())
.grantType(OAuth2Constants.PASSWORD)
.clientId("myclient")
.clientSecret("0a53569564d2a748a0a5482699")
.resteasyClient(new ResteasyClientBuilder().connectionPoolSize(10).build()).build();
}
return keycloak;
}
In service class
UsersResource usersResource = KeycloakConfig.getInstance(user).realm("myrealm").users();
CredentialRepresentation credentialRepresentation = createPasswordCredentials(user.getPassword());
UserRepresentation kcUser = new UserRepresentation();
kcUser.setUsername(user.getEmail());
kcUser.setCredentials(Collections.singletonList(credentialRepresentation));
kcUser.setFirstName(user.getFirstName());
kcUser.setLastName(user.getLastName());
kcUser.setEmail(user.getEmail());
kcUser.setEnabled(true);
kcUser.setEmailVerified(false);
javax.ws.rs.core.Response response = usersResource.create(kcUser);
System.out.println("kcUser: " + kcUser.toString());
System.out.printf("Repsonse: %s %s%n", response.getStatus(), response.getStatusInfo());
in properties file
keycloak.realm=myrealm
keycloak.auth-server-url=http://localhost:8080/auth
keycloak.resource=myclient
keycloak.credentials.secret=0a5356444d26a748a0a5482699
keycloak.ssl-required= external
keycloak.use-resource-role-mappings= true
keycloak.bearer-only=true
keycloak.principal-attribute=preferred_username

Due to missing imports I assume that KeycloakBuilder is comming from package org.keycloak.admin.client.KeycloakBuilder
Based on your provided code examples you are trying to instantiate a Keycloak instance with an user you just want to create. That user is lack of the privileges to create a new user.
That said you need to create an Keycloak instance with an user that has admin rights / roles. At least "manage-users, view-clients, view-realm, view-users"
keycloak = KeycloakBuilder.builder()
.serverUrl(KEYCLOAK_SERVER_URL)
.realm("master")
.clientId("admin-cli")
.username("admin")
.password("admin")
.build();
With admin access you are able to create users for several realms.
// Create a new user
final UserRepresentation newUser = new UserRepresentation();
newUser.setUsername("username");
newUser.setFirstName("fristname");
newUser.setLastName("lastname");
newUser.setEmail("username#example.com");
newUser.setEnabled(true);
// Now you need to get the realm you want the user to add to and the user ressource
final UserResource realm = keycloak.realm("nameOfTheRealm").users();
// Create the user for the realm
final Response createUserResponse = userRessource.create(newUser);
// check response if everything worked as expected

Make sure the admin account is created under 'myrealm'. You cannot use the default admin account (master realm) to create the user for 'myrealm'

In order for a user to have the right to manipulate and call Keycloak's API, you must set the client role for that user
Step 1: User should create a administrators like "admin" password "abc123" in your client "myrealm"
Step 2: Click admin user after move to Roles mapping tab and assign some necessary permissions to admin user as below
Step 3: Replace password in .username(user.getUsername()).password(user.getPassword()) with "abc123"
Hope to help you!

Related

Keycloak - require email verification before creating the user

We are evaluating Keycloak to replace Forgerock for user registration
Our current workflow provides a registration screen. On submitting the registration form, an email is sent to the user to verify their email and activate their account. The link in the email confirms the user registration before creating the user in forgerock.
My questions:
Is there a way to create the user after the email verification as a confirmation?
I have this implementation but sendVerifyEmail it is just for checking the email and basically the user can login even if he/she didn't check the email
Keycloak keycloak = KeycloakBuilder
.builder()
.serverUrl(KEYCLOAK_URL)
.realm(KEYCLOAK_REALM)
.username(KEYCLOAK_USER)
.password(KEYCLOAK_PASSWORD)
.clientId(KEYCLOAK_ADMIN_CLI)
.build();
CredentialRepresentation credential = createPasswordCredentials(userRegistrationRequest.getPassword());
UserRepresentation user = new UserRepresentation();
user.setEmail(userRegistrationRequest.getEmail());
user.setCredentials(Collections.singletonList(credential));
user.setEnabled(true);
// Get realm
RealmResource realmResource = keycloak.realm(KEYCLOAK_REALM);
UsersResource usersResource = realmResource.users();
// Create user (requires manage-users role)
Response response = usersResource.create(user);
String userId = CreatedResponseUtil.getCreatedId(response);
System.out.println("Response: " + response.getStatusInfo());
System.out.println(userId);
UserResource u = realmResource.users().get(userId);
u.sendVerifyEmail();
This is late though but you can set the user representation email verified to false when creating the user. So they won't be able to access until they verify the email.

Unable to find the group information of the logged in user -JWT token OKTA

I am new to Okta so apologies if my questions are not clear.
So what I want to do is basically parse the JWT token generated by
okta and extract the group information of the logged in user
associated with it.
I am under the impression that this information should be there in the
OidcUser object. I do see user name/email id / token validity etc
information inside this object. Unfortunately I can't see group id
which I need for further processing.
#RequestMapping("/")
public String hello(#AuthenticationPrincipal OidcUser user){
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, Object> entry : user.getClaims().entrySet()) {
sb.append(entry.getKey() + ":" + entry.getValue().toString());
sb.append("\n");
}
sb.append("|");
sb.append(user.getClaims());
return sb.toString();
}
Here is my okta plugin inside spring boot
okta.oauth2.issuer=https://dev-XXXXXXXX.okta.com/oauth2/default
okta.oauth2.client-id=XXXXXXXXXX
okta.oauth2.client-secret=XXXXXXXXXXXX
I am wondering if my approach is proper and what more I need to do to extract User group from Okta JWT token.
To get user groups you need to make an additional request to /userinfo endpoint, assuming you requested groups scope during /authorize call.
Please see a guide here
Not exactly spring-boot response, but it's always beneficial to know how things work under-the-hood

WebAPI scope authorization through azure app registrations

I am trying to control authorization via app registrations in Azure.
Right now, I have two app registrations set up.
ApiApp
ClientApp
ApiApp is set up with the default settings, but I have added this to the manifest:
"oauth2Permissions": [
{
"adminConsentDescription": "Allow admin access to ApiApp",
"adminConsentDisplayName": "Admin",
"id": "<guid>",
"isEnabled": true,
"type": "User",
"userConsentDescription": "Allow admin access to ApiApp",
"userConsentDisplayName": "Admin",
"value": "Admin"
},
...
]
In the client app registration, I have all the defaults, but I added:
In the keys, a password for authenticating the app against AD
In required permissions, I added ApiApp and required the delegated permission "Admin." I saved that, clicked done, then I clicked "Grant Permissions" to make sure the permissions had a forced update.
In my client app, it uses this code for authentication purposes:
...
var context = new AuthenticationContext(authority);
var clientCredentials = new ClientCredential(<clientId>, <clientSecret>);
var result = await context.AcquireTokenAsync(<apiAppUri>, clientCredentials);
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", result.AccessToken);
var webResult = await client.GetAsync(<api uri>);
My ApiApp is just using the built in authorization if you select work or school accounts when you create a Web API project:
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters {
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
},
});
}
This works:
[Authorize]
public class ValuesController : ApiController
These do not work:
[Authorize(Users = "Admin")]
public class ValuesController : ApiController
or
[Authorize(Roles= "Admin")]
public class ValuesController : ApiController
Based on what I'm reading, I believe I have everything set up appropriately except the ApiApp project itself. I think I need to set up the authorization differently or with extra info to allow the oauth2Permission scopes to be used correctly for WebAPI.
What step(s) am I missing to allow specific scopes in WebAPI instead of just the [Authorize] attribute?
I used Integrating applications with Azure Active Directory to help me set up the app registrations, along with Service to service calls using client credentials , but I can't seem to find exactly what I need to implement the code in the Web API part.
UPDATE
I found this resource: Azure AD .NET Web API getting started
It shows that you can use this code to check out scope claims:
public IEnumerable<TodoItem> Get()
{
// user_impersonation is the default permission exposed by applications in Azure AD
if (ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/scope")
.Value != "user_impersonation")
{
throw new HttpResponseException(new HttpResponseMessage {
StatusCode = HttpStatusCode.Unauthorized,
ReasonPhrase = "The Scope claim does not contain 'user_impersonation' or scope claim not found"
});
}
...
}
However, the claims I get do not include any scope claims.
You have to use appRoles for applications, and scopes for applications acting on behalf of users.
As per GianlucaBertelli in the comments of Azure AD, Scope-based authorization:
...in the Service 2 Service scenario, using the client credentials flow you won’t get the SCP field. As you are not impersonating any user, but the calling App (you are providing a fixed credential set).
In this case you need to use AppRoles (so Application permissions, not delegated) that results in a different claim. Check a great how-to and explanation here: https://joonasw.net/view/defining-permissions-and-roles-in-aad.
In the link he provides, it discusses appRoles in the application manifest.
The intention behind the resources I was looking at before was to allow users to login to the client application, and then the client application authenticates against the API on behalf of the user. This is not the functionality I was trying to use -- simply for a client application to be able to authenticate and be authorized for the API.
To accomplish that, you have to use appRoles, which look like this in the application manifest:
{
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"displayName": "Read all todo items",
"id": "f8d39977-e31e-460b-b92c-9bef51d14f98",
"isEnabled": true,
"description": "Allow the application to read all todo items as itself.",
"value": "Todo.Read.All"
}
]
}
When you set the required permissions for the client application, you choose application permissions instead of delegated permissions.
After requiring the permissions, make sure to click the "Grant Permissions" button. To grant application permissions, it requires an Azure Active Directory admin.
Once this is done, requesting an access token as the client application will give you a "roles" claim in the token, which will be a collection of string values indicating which roles the application holds.

Spring Security - Further Restricting Methods

I have an application that is authorizing an OAuth2 token with Spring Security. Using the #PreAuthorize tag, I can easily make sure that a user has a permission before allowing them to access a method:
#PreAuthorize("#oauth2.hasScope('account.read')")
public void getAccount(int accountId);
{
//return account
}
This works great at restricting users without the account.read permission from accessing this method.
The only problem is now any user with this permission can access any account. I want to restrict the users to only access their own account. I'm sure this is a common scenario. How do other applications deal with this?
So, the question here - how would system know if account belongs to user?
The answer would be that you are probably storing User<->Account relationship in the database. The most simple solution would be to do the check right in your method:
#PreAuthorize("#oauth2.hasScope('account.read')")
public Account getAccount(int accountId) {
// get account from db
Account account = repository.findById(accountId);
// you will need a little helper to get your User from
//Spring SecurityContextHolder or whatever there for oauth2
User user = securityManager.getCurrentUser();
if (account.belongs(user)) {
return account;
} else {
throw new UnathorizedException("User is not authorized to view account");
}
}
Upd. one of possible improvements may be to first get the user, get id from it and do a repository.findByIdAndUserId(accountId, userId) or somthing like that. (or even repositoryFindByIdAndUser(accountId, user))

User Granted Authorities are always : ROLE_ANONYMOUS?

I am using the following method to make a programmatic login after registration
private void autoLogin(User user,
HttpServletRequest request)
{
GrantedAuthority[] grantedAuthorities = new GrantedAuthority[] { new GrantedAuthorityImpl(
"ROLE_ADMIN") };
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
user.getUsername(), user.getPassword(),grantedAuthorities);
// generate session if one doesn't exist
request.getSession();
token.setDetails(new WebAuthenticationDetails(request));
Authentication authenticatedUser = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authenticatedUser);
}
the user is authenticated but always has the ROLE_ANONYMOUS I don't know why ?
any ideas ?
This behaviour looks very strange. Javi suggests to persist security context into session manually, but it should be done automatically by Spring Security's SecurityContextPersistenceFilter.
One possible cause I can imagine is filters = "none" in <intercept-url> of your registration processing page.
filters = "none" disables all security filters for the specified URL. As you can see, it may interfere with other features of Spring Security. So, the better approach is to keep filters enabled, but to configure them to allow access for all users. You have several options:
With old syntax of access attribute (i.e. without <http use-expressions = "true" ...>):
access = "ROLE_ANONYMOUS" allows access for non-authenticated users, but denies for the authenticated ones
To allow access for all users you may write access = "IS_AUTHENTICATED_ANONYMOUSLY, IS_AUTHENTICATED_FULLY, IS_AUTHENTICATED_REMEMBERED"
Using new Spring Expression Language-based syntax (<http use-expressions = "true" ...>) you simply write access = "true" to allow access for all users (but other <intercept-url>s should use this syntax too).
I had a similar issue and I had to set manually to the session this after doing the authentication.
request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());
Try it.

Resources