Skip to content

Library providing Keycloak Security for KumuluzEE framework

License

Notifications You must be signed in to change notification settings

Jamsek-m/kee-auth

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

45 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

KeeAuth Library

Build Status beta Last commit GitHub license

Library providing OpenId Connect security for KumuluzEE framework

Version 1.x.x

If you are looking for version 1.x.x of KeeAuth, go to 1.x.x branch

Requirements

Library is compatible with Java 11+

Usage

Project is still in beta. See beta projects.

Import library in your project:

<dependency>
    <groupId>com.mjamsek.auth</groupId>
    <artifactId>kee-auth</artifactId>
    <version>2.0.0-SNAPSHOT</version>
</dependency>

Configuration

OpenId Connect

Autoconfiguration

Library can use provider's well-known endpoint to read configuration or retrieve keys.

kee-auth:
  oidc:
    auto-configure: true
    well-known-url: https://keycloak.example.com/auth/realms/test-realm/.well-known/openid-configuration

You can also manage retrieving jwks from jwks_url, by completely disabling fetching from a remote source:

kee-auth:
  oidc:
    use-jwks-uri: false

or by providing own jwks_url (endpoint from well-known config is then ignored):

kee-auth:
  oidc:
    use-jwks-uri: true
    jwks-url: https://keycloak.example.com/auth/realms/test-realm/protocol/openid-connect/certs

Json Web Token

Token time leeway

You can configure default time leeway (1 second), by setting key:

kee-auth:
  jwt:
    time-leeway: 2

Token claims mapping

If your token claims are not mapped according to OIDC specification, you can provide your own mappings for AuthContext:

kee-auth:
  jwt:
    claims:
      id: sub
      email: email
      username: preferred_username
      scope: scope

Token keys

Keys if not retrieved from JWKS endpoint need to be specified in configuration:

kee-auth:
  jwt:
    keys:
      - kid: g7j
        alg: HS256
        secret: secretkey
      - kid: d5i
        alg: RS256
        n: 'qtzjj3KuFldrDL3rpIsKoEjzxwHQEUmzQNHoCP8CYiA-29q-Tqt8s0ALg5ruK-UH5pBJxtixEuYG0jkNcHFLOfJz45W2Lc1kjYtOQbjMEDewrCG2pDDT-kBbjs_GaaOINQfV5e1Duv9BYpHSaQtrin-yeOX4Lu5U6qTRzuSo-bO29dB6Vr5Lj8vhEUm5vQE9RvsM1ZyXwGV6LLwi-cerfEhKgHhfoUzGuZqB4EI74LoyPytxTafb53tSz2eccALjTdfor03NUae89qiOC11FGw0gVteoplkW7nH6dVMwfTfByGb6fTzYy2Yx_30kCO6yNwALQdDujJNxs2Y332w'
        e: 'ATEF'
      - kid: 7e9
        alg: RS256
        x5c: 'MIIClTCCAX0CBgF3ISs0kjANBgkqhkiG9w0BAQsFADAOMQwwCgYDVQQDDANrcnYwHhcNMjEwMTIwMTg1ODM1WhcNMzEwMTIwMTkwMDE1WjAOMQwwCgYDVQQDDANrcnYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCq3OOPcq4WV2sMveukiwqgSPPHAdARSbNA0egI/wJiID7b2r5Oq3yzQAuDmu4r5QfmkEnG2LES5gbSOQ1wcUs58nPjlbYtzWSNi05BuMwQN7CsIbakMNP6QFuOz8Zpo4g1B9Xl7UO6/0FikdJpC2uKf7J45fgu7lTqpNHO5Kj5s7b10HpWvkuPy+ERSbm9AT1Ha+wzVnJfAZXosvCL5w4VFl8SEqAeF+hTMa5moHgQjvgujI/K3FMOC4De1LPZ5xwAuNN1+ivTc1RIJ32qI4LXUUbDSBW16imWRbucfp1UzB9N8HIZvp9PNjLZjH/fSQI7rI3AAtB0O6Mk3GzZiffbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAKAT+e2wVNEJld5CCtpqN13fvCJw7Yc2HZdeg648JqkrLOVyZltynMA2VG6NM4sDwQcH3Cb75nPcfND+rUrTSDiu0eQ3xfCh4pfsqvh8EEQ95yrGGm91McdGlNt24IUZNfGFzGZs/cCfuxoQUfpMCSSlVN/6SFpm8E5wHMYP9ALR+Aw8eVWMOOpDDzQWDZHo7lxkz4rMuaeWqOggAsdsrEblpWoU52Mxy02x+I8GUJdxjwL1atRI1yuUp7LkZ1O+NsdedOvHpUlDGRPIbF2VfeurrIIs0y2Pz1TdCR5eSxjVHPleiabaKjWft8qUysGGptTr+FmGj9KGad4L8='

Currently, only Hmac with SHA-1 (HS256, HS384, HS512) and RSA (RS256, RS384, RS512) are supported algorithms for token verification.

Client mapping

You can provide optional client mapping to decouple client id with code.

kee-auth:
  clients:
    client-name-1: client-id-1
    client-name-2: client-id-2

Authentication and authorization

To enable security in resource class, you must annotate it with @SecureResource. Then you can annotate methods in this class with appropriate annotations. You can also put annotations on class. This means that non-annotated methods will take class-based access level.

// Enable security in this class
@SecureResource
// All methods in this class need user to be authenticated (optional, you can put annotations on methods only)
@AuthenticatedAllowed
@RequestScoped
@Path("/customers")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class SampleResource {
    
    @GET
    // only admins or salesmen can retrieve list of customers
    @RolesAllowed({"salesman", "admin"})
    public Response getCustomers() {
        // ... 
        return Response.ok(customers).build();
    }
    
    @GET
    // This method uses class annotated access level - authentication only
    public Response getCustomerDetails() {
        // ... 
        return Response.ok(customerDetails).build();
    }
    
    @POST
    // This method overrides class based annotation and is public - no authentication required
    @PublicResource
    public Response notifyCustomer() {
        // ... 
        return Response.ok().build();
    }
    
}

Annotation types

  • @AuthenticatedAllowed: to access this method a user (any valid user) must present valid JWT
  • @RolesAllowed({"dev"}): to access this method a user must have role 'dev' [1]
  • @RolesAllowed(value = {"dev"}, clientName = "test-service"): to access this method a user must have role 'dev' on a specified client (if identity provider supports it) [1]

If you want to expose single method in otherwise protected resource class you can use @PublicResource annotation on method, you want to make public.

[1]: More in resolvers.

Roles resolvers

Roles are resolved using role resolver. Library comes pre-packaged with default implementation of role resolver, however, you can also use another provided resolver, or write your own.

Read more

Security context

Security context

You can retrieve data about user trying to access endpoint by injecting AuthContext object:

@Inject
private AuthContext authContext;

Alternatively, you can also retrieve raw token:

@Inject
@Token
private String rawToken;

In unsecured (public) endpoints, authContext will not be available. Therefore, it is advisable to check if user is authenticated before using its methods:

if (authContext.isAuthenticated()) {
    // ...
}

Auth context provides following data:

  • user id (sub claim)
  • username (preferred_username claim)
  • email (email claim)
  • scopes (scope claim)
  • authenticated flag
  • other claims from token
  • raw token

Creating context manually

If for some reason, you cannot use CDI, you can also manually construct AuthContext:

String jwt = "..."; // JWT from request
AuthContext context = KeeAuth.createContext(jwt);

If for some reason, context cannot be constructed (i.e. expired jwt), it will return empty context (authenticated flag is set to false).

Parsing only JWT

If you do not need auth context, you can also parse json web token to claims map:

String jwt = "..."; // JWT from request
Map<String, Object> tokenClaims = KeeAuth.parseJsonWebToken(jwt)

Client credentials flow

KeeAuth provides client for performing calls with client credentials flow.

Configuration

To configure client, you need to provide following configuration:

kee-auth:
  oidc:
    client-credentials:
      client-id: test-client
      client-secret: e7820cf7-fbfb-4397-b156-91b15a2e3
      # Can be omitted if autoconfiguration is enabled
      token-url: https://keycloak.example.com/auth/realms/test-realm/protocol/openid-connect/token

Usage

To use client for client credentials flow, use ServiceCallClient#call, where you provide Function that receives one argument (that is access token) and returns retrieved data.

public List<User> getAccounts() {
    String serviceCallUrl = "https://keycloak.example.com/auth/admin/realms/test-realm/users";
    
    return ServiceCallClient.call(token -> {
        Response response = ClientBuilder.newClient()
            .target(serviceCallUrl)
            .request(MediaType.APPLICATION_JSON)
            .header("Authorization", "Bearer " + token)
            .get();
            
        if (response.getStatus() >= 400) {
            String error = response.readEntity(String.class);
            LOG.error(error);
            throw new ServerErrorException(500);
        } else {
            return response.readEntity(new GenericType<List<User>>() {});
        }
    });
}

Sample

Samples can be found on this page

Changelog

Changes can be viewed on releases page on GitHub.

License

MIT

Beta projects

For beta projects you need to include repository in your pom.xml:

<repositories>
    <repository>
        <id>mjamsek-beta</id>
        <url>https://nexus.mjamsek.com/repository/mjamsek-beta</url>
    </repository>
</repositories>

No credentials are required.

For more information about beta program, see mjamsek.com/beta.

About

Library providing Keycloak Security for KumuluzEE framework

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages