the call i use to generate token:
export access_token=$(curl -X POST http://my keycloak server/auth/realms/my realm/protocol/openid-connect/token -H 'content-type: application/x-www-form-urlencoded' -d "username=my username" -d "password=my password" -d 'grant_type=password' -d "client_id=application-cards" | jq --raw-output '.access_token')
That successed i a get the token i use to post to my RolesAllowed anotated method (i've even tried to remove the annotation temporally to verify that this method responds and it responds and now i put it again)
Here is my application.properties:
quarkus.datasource.url=jdbc:postgresql://my server:5432/applicationcards
quarkus.datasource.driver=org.postgresql.Driver
quarkus.datasource.username=db username
quarkus.datasource.password=db password
quarkus.datasource.min-size=3
quarkus.datasource.max-size=13
quarkus.log.console.color=false
quarkus.log.category."it.previnet.applicationcards".level=INFO
quarkus.log.console.level=TRACE
quarkus.oidc.auth-server-url=http://my keycloak server/auth/realms/my realm
quarkus.oidc.client-id=application-cards
quarkus.oidc.credentials.secret=secret
quarkus.log.category."io.quarkus.smallrye.jwt".level=TRACE
quarkus.log.category."io.undertow.request.security".level=TRACE
quarkus.log.category."io.smallrye.jwt".level=TRACE
And here my pom.xml:
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<dependency>
<groupId>it.previnet.applicationcards</groupId>
<artifactId>applicationcards-service-adapter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>it.previnet.applicationcards</groupId>
<artifactId>applicationcards-presentation-adapter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>it.previnet.applicationcards</groupId>
<artifactId>applicationcards-repository-adapter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-agroal</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j-log4j12.version}</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>${commons-beanutils.version}</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>${commons-collections4.version}</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson-datatype-jsr310.version}</version>
<type>jar</type>
</dependency>
<!-- Testing: -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
The keycloak server has a user federation with LDAP but as i can generate a token i assume that the configuration of the server is correct. The problem is that i can't read any log both in ther server output and my quarkus application console output, so i can't figure out where the problem could be.
My
Right now if the token contains a groups claim then it will be used to populate the principal roles but by default KC does not use this claim. In the newer KC version you can set the MP-JWT option somewhere for it to include the roles in the groups claim.
The principal created by the OIDC module can probably be customized as well
Directly from admin console the server version i'm using:
Server Version 6.0.1
@tassadar81 It looks like your quarkus.oidc.auth-server-url is missing the realm. With the new extension, it should be http://my keycloak server/auth/realms/YOUR_REALM_NAME
However, based on your quarkus-resteasy-jackson dependency, it looks like you will run in to the same problem as me: #4672
@tassadar81 FYI: in Keycloak 7.0.1 I had to go to Client -> Mappers -> Add Builtin and add groups to get the roles added to the groups claim. After that, jsonWebToken.getGroups() returns the groups as expected.
Note though, that I did not need the group claim for@RolesAllowed. I guess for authorisation, the roles are read from the claim realm_access.roles.
@tassadar81 It looks like your
quarkus.oidc.auth-server-urlis missing the realm. With the new extension, it should behttp://my keycloak server/auth/realms/YOUR_REALM_NAME
My configuration has the realm, i'll edit the original message to improve clarity
However, based on your
quarkus-resteasy-jacksondependency, it looks like you will run in to the same problem as me: #4672
oh bad luck :(
Here is my token decoded:
{
"jti": "7cb71df3-ee95-494b-aeb8-8eb03bf479f0",
"exp": 1571643702,
"nbf": 0,
"iat": 1571643402,
"iss": "http://my keycloak server/auth/realms/my realm",
"aud": "account",
"sub": "dfcc6a99-a331-4a24-a807-7773ea27ce0a",
"typ": "Bearer",
"azp": "application-cards",
"auth_time": 0,
"session_state": "6de76935-3c94-45f7-a926-87850d8afd96",
"acr": "1",
"allowed-origins": [
"http://my pc url where my quarkus app is nunning:8080"
],
"realm_access": {
"roles": [
"offline_access",
"uma_authorization"
]
},
"resource_access": {
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
},
"application-cards": {
"roles": [
"tipoapplicazione-read"
]
}
},
"scope": "profile email",
"email_verified": false,
"name": "My name",
"groups": [
"offline_access",
"uma_authorization"
],
"preferred_username": "my ldap username",
"given_name": "my name",
"family_name": "my surname",
"email": "[email protected]"
}
@tassadar81 FYI: in Keycloak 7.0.1 I had to go to Client -> Mappers -> Add Builtin and add
groupsto get the roles added to the groups claim. After that,jsonWebToken.getGroups()returns the groups as expected.
Note though, that I did not need the group claim for@RolesAllowed. I guess for authorisation, the roles are read from the claimrealm_access.roles.
Added the mapper you told me but nothing has changed
@tassadar81 what do you have in @RolesAllowed ?
@sberyozkin i've @RolesAllowed("tipoapplicazione-read")
i didn't get notice the the error now is a bit changed, now it shows a 403:
>
< HTTP/1.1 403 Forbidden
< Content-Length: 34
< Content-Type: text/html;charset=UTF-8
<
{ [34 bytes data]
100 34 100 34 0 0 311 0 --:--:-- --:--:-- --:--:-- 311Access forbidden: role not allowed
Sorry :)
@tassadar81 np, but what is the value of the RolesAllowed annotation ?
By the way, the adapter will work with the arbitrary claims containing the roles info, KC users won't have to be creating groups mappings, it is only currently the solution which should work
@sberyozkin the value is tipoapplicazione-read as i written in the previous comment
@tassadar81 but that one is inside the custom application-cards claim, so this is why it is not available inside the groups. As I said, the adapter should be able to get the roles content from everywhere in the token, but for now it is not possible
@sberyozkin ok now i've a new token solved as:
{
"jti": "f6e7065b-314a-4e9e-a7cf-16dcee3b1d02",
"exp": 1571655748,
"nbf": 0,
"iat": 1571655448,
"iss": "http://my keycloak server/auth/realms/my realm",
"aud": "account",
"sub": "dfcc6a99-a331-4a24-a807-7773ea27ce0a",
"typ": "Bearer",
"azp": "application-cards",
"auth_time": 0,
"session_state": "7e29d459-bc67-4a15-ba03-ab9f740cbe7b",
"acr": "1",
"allowed-origins": [
"http://my pc url where my quarkus app is nunning:8080"
],
"realm_access": {
"roles": [
"offline_access",
"uma_authorization"
]
},
"resource_access": {
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
},
"application-cards": {
"roles": [
"tipoapplicazione-read"
]
}
},
"scope": "profile email",
"email_verified": false,
"name": "My name",
"groups": [
"tipoapplicazione-read"
],
"preferred_username": "my ldap username",
"given_name": "my name",
"family_name": "my surname",
"email": "[email protected]"
}
and my application rest has RolesAllowed("tipoapplicazione-read")
But i still get
< HTTP/1.1 403 Forbidden
< Content-Length: 34
< Content-Type: text/html;charset=UTF-8
<
{ [34 bytes data]
@tassadar81 I'll have to investigate, there is some inconsistent code at the VertxOAuth2IdentityProvider level at the moment, a groups aware Principal is registered, but then the roles are extracted from realm_access/roles after all :-), sorry I did not spot it earlier.
This is a problem because if one injects Principal then its isUserInRole would return the result inconsistent with what is added to the security identity roles.
So please get that custom role listed inside realm_access/roles and in meantime I'll investigate how to fix it. Thanks !
Never mind, Principal does not have this method, only SecurityContext. But the inconsistency remains, we just need to sync the info a bit
@sberyozkin as now is very weird in the case in the same realm you have different applications. But ok, i'll stay tuned on topic thanks
@tassadar81 I agree; stay tuned, I'll fix it for 0.27.0
@sberyozkin I think that explains some odd inconsistency I've noted as well:
Using SecurityIdentity.getRoles() I did indeed get all roles from the token, while using JsonWebToken.getGroups() remained empty.
@xfh Right, we have to consolidate things a bit. In smallrye-jwt we map the arbitrary roles claim to groups, so the same concept should work here. I think we'll have to provide some API as well for the users to do some complex mappings. but in most cases just setting a property should be enough, with the groups and the KC path to the roles, and scopes to be supported by default.
@sberyozkin Regarding custom role mapping, users can provide a RoleDecoder, right ? Not sure though if that is the best API we can offer (although it is quite simple).
For KC path to roles, I think we are missing the mapping of "client roles". I've considered that in my initial set of changes to the code flow.
RoleDecoder is an Elytron API, so it only works with the elytron based modules.
At the moment you can provide a io.quarkus.security.identity.SecurityIdentityAugmentor CDI bean in your application, inject the token using MP JWT, and then populate the identity however you want.
@sbery
@tassadar81 I agree; stay tuned, I'll fix it for 0.27.0
@sberyozkin Regarding custom role mapping, users can provide a
RoleDecoder, right ? Not sure though if that is the best API we can offer (although it is quite simple).For KC path to roles, I think we are missing the mapping of "client roles". I've considered that in my initial set of changes to the code flow.
Most of all i think that user needs what is planned on issue #4480. With bearer authentication fixed on 0.27 what are the options for user to secure a web application using quarkus? I mean there's any kind of security interceptor to provide a custom login page? Then a security filter api to generate the token to use to the next requests?
@tassadar81 Could you elaborate more why you would need a login page in your application given that #4480 is related to OIDC Authorization Code Flow for authenticating users? That would mean redirecting your users to some OP where the login page and authentication flow happen.
OIDC Authorization Code Flow
@pedroigor Yes, as #4480 is still to be implemented if i've understood correctly, for my web application i'm developing i need a login process inside the application. So i was asking which options quarkus offers now to get this using bearer auth. In the guides i've found just how to secure rest backend methods.
I'd like to write some interceptors that will manage this that cound be putted in a different jar to include in my pom.xml
@stuartwdouglas @pedroigor so it is super good, we already have io.quarkus.security.identity.SecurityIdentityAugmentor for the users who need to do something advanced; I'll just try ti do some tweaks to ensure it it works consistently OOB for the major cases (when groups is available, for KC tokens, for scopes, and for some arbitrary claims using a smallrye.jwt.groups,path property - would have to copy a bit of code for it to work :-) )
@tassadar81 As Pedro said we'll provide a standard based solution to have a server web app user redirected to IDP such as Keycloak and returned back; as part of that PR we'll let users differentiate between the authorization only and web flows
@tassadar81 As Pedro said we'll provide a standard based solution to have a server web app user redirected to IDP such as Keycloak and returned back; as part of that PR we'll let users differentiate between the authorization only and web flows
@sberyozkin ok i've missed such possibility, where i can find a guide to configure both rest methods and web flows? I was reading this guide but just rest services are present
[EDITED] @sberyozkin i read again what you wrote so you're confirming that as now there's no web flows option. There's any roadmap about?
@tassadar81 sure, please see #4480
I should have the PR coming in once I complete the tests