Skip to content

Commit

Permalink
Adding Module for token-handling (#60)
Browse files Browse the repository at this point in the history
* Fixing redundant formatted call

* Adding portal-authentication.-token module

* Fixing maven-build
  • Loading branch information
cuioss authored Oct 2, 2024
1 parent 83ce234 commit 5f27d22
Show file tree
Hide file tree
Showing 28 changed files with 1,090 additions and 51 deletions.
9 changes: 8 additions & 1 deletion bom/pom.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>de.cuioss.portal</groupId>
Expand Down Expand Up @@ -36,6 +37,12 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.cuioss.portal.authentication</groupId>
<artifactId>portal-authentication-token</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<!-- Configuration-->
<dependency>
<groupId>de.cuioss.portal.core</groupId>
Expand Down
1 change: 1 addition & 0 deletions modules/authentication/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<module>portal-authentication-mock</module>
<module>portal-authentication-oauth</module>
<module>portal-authentication-dummy</module>
<module>portal-authentication-token</module>
</modules>
<dependencies>
<!-- Provided -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,51 +15,37 @@
*/
package de.cuioss.portal.authentication.oauth.impl;

import static de.cuioss.tools.string.MoreStrings.emptyToNull;
import static java.net.URLEncoder.encode;
import static java.util.Objects.requireNonNull;

import java.io.IOException;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import jakarta.servlet.http.HttpServletRequest;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import de.cuioss.portal.authentication.AuthenticatedUserInfo;
import de.cuioss.portal.authentication.facade.AuthenticationSource;
import de.cuioss.portal.authentication.facade.BaseAuthenticationFacade;
import de.cuioss.portal.authentication.facade.PortalAuthenticationFacade;
import de.cuioss.portal.authentication.model.BaseAuthenticatedUserInfo;
import de.cuioss.portal.authentication.oauth.LoginPagePath;
import de.cuioss.portal.authentication.oauth.Oauth2AuthenticationFacade;
import de.cuioss.portal.authentication.oauth.Oauth2Configuration;
import de.cuioss.portal.authentication.oauth.Oauth2Service;
import de.cuioss.portal.authentication.oauth.OauthAuthenticationException;
import de.cuioss.portal.authentication.oauth.OauthRedirector;
import de.cuioss.portal.authentication.oauth.OidcRpInitiatedLogoutParams;
import de.cuioss.portal.authentication.oauth.Token;
import de.cuioss.portal.authentication.oauth.*;
import de.cuioss.tools.collect.CollectionBuilder;
import de.cuioss.tools.logging.CuiLogger;
import de.cuioss.tools.net.UrlParameter;
import de.cuioss.tools.string.MoreStrings;
import de.cuioss.tools.string.Splitter;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import jakarta.servlet.http.HttpServletRequest;

import java.io.IOException;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.*;

import static de.cuioss.tools.string.MoreStrings.emptyToNull;
import static java.net.URLEncoder.encode;
import static java.util.Objects.requireNonNull;

/**
* Default implementation of {@link Oauth2AuthenticationFacade}. Uses
Expand Down Expand Up @@ -167,10 +153,10 @@ private void sendRedirect(final String scopes, final String idToken) {
}

private Optional<AuthenticatedUserInfo> triggerAuthenticate(final List<UrlParameter> parameters,
final String scopes) {
final var code = parameters.stream().filter(parameter -> "code".equals(parameter.getName())).findAny();
final var state = parameters.stream().filter(parameter -> "state".equals(parameter.getName())).findAny();
final var error = parameters.stream().filter(parameter -> "error".equals(parameter.getName())).findAny();
final String scopes) {
final var code = parameters.stream().filter(parameter -> "code" .equals(parameter.getName())).findAny();
final var state = parameters.stream().filter(parameter -> "state" .equals(parameter.getName())).findAny();
final var error = parameters.stream().filter(parameter -> "error" .equals(parameter.getName())).findAny();
if (state.isPresent()) {
if (code.isPresent()) {
return handleTriggerAuthenticate(scopes, code.get(), state.get());
Expand All @@ -193,7 +179,7 @@ private Optional<AuthenticatedUserInfo> triggerAuthenticate(final List<UrlParame

@SuppressWarnings("squid:S3655") // already checked
private Optional<AuthenticatedUserInfo> handleTriggerAuthenticate(final String scopes, final UrlParameter code,
final UrlParameter state) {
final UrlParameter state) {
final var servletRequest = servletRequestProvider.get();
LOGGER.debug("code and state parameter are present");
final AuthenticatedUserInfo sessionUser;
Expand Down
28 changes: 28 additions & 0 deletions modules/authentication/portal-authentication-token/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
= portal-authentication-token

== What is it?
Provides some convenience structures for dealing with JTW-Token.

It is essentially a wrapper around the types from io.smallrye:smallrye-jwt.

The core functionality is the simplified configuration of checking the signature of a given token, by looking up the corresponding public-keys from an oauth-server, tested with keycloak.

== Maven Coordinates

[source, xml]
----
<dependency>
<groupId>de.cuioss.portal.authentication</groupId>
<artifactId>portal-authentication-token</artifactId>
</dependency>
----

== Usage

The central objects are:

* link:src/main/java/de/cuioss/portal/authentication/token/JwksAwareTokenParser.java[Configuration of the io.smallrye.jwt.auth.principal.JWTParser]

* link:src/main/java/de/cuioss/portal/authentication/token/ParsedAccessToken.java[ParsedAccessToken]

* link:src/main/java/de/cuioss/portal/authentication/token/ParsedIdToken.java[ParsedIdToken]
68 changes: 68 additions & 0 deletions modules/authentication/portal-authentication-token/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>de.cuioss.portal.authentication</groupId>
<artifactId>authentication</artifactId>
<version>1.1.0-SNAPSHOT</version>
</parent>
<artifactId>portal.authentication.token</artifactId>
<properties>
<version.smallrye-jwt>4.5.2</version.smallrye-jwt>
<version.parsson>1.1.6</version.parsson>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-jwt</artifactId>
<version>${version.smallrye-jwt}</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-jwt-build</artifactId>
<version>${version.smallrye-jwt}</version>
<scope>test</scope>
</dependency>
<!-- Implementation of jakarta.json-api-->
<dependency>
<groupId>org.eclipse.parsson</groupId>
<artifactId>parsson</artifactId>
<version>${version.parsson}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
</dependency>
<dependency>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-jwt</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-jwt-build</artifactId>
</dependency>
<dependency>
<groupId>de.cuioss.portal.test</groupId>
<artifactId>portal-core-unit-testing</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver3-junit5</artifactId>
</dependency>
<!-- Implementation of jakarta.json-api-->
<dependency>
<groupId>org.eclipse.parsson</groupId>
<artifactId>parsson</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package de.cuioss.portal.authentication.token;

import de.cuioss.tools.logging.CuiLogger;
import io.smallrye.jwt.auth.principal.DefaultJWTParser;
import io.smallrye.jwt.auth.principal.JWTAuthContextInfo;
import io.smallrye.jwt.auth.principal.JWTParser;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.ToString;
import lombok.experimental.Delegate;

/**
* Variant of {@link JWTParser} that will be configured for remote loading of the public-keys.
* They are needed to verify the signature or the token.
*
* @author Oliver Wolff
*/
@ToString
@EqualsAndHashCode
public class JwksAwareTokenParser implements JWTParser {

private static final CuiLogger LOGGER = new CuiLogger(JwksAwareTokenParser.class);

@Delegate
private final JWTParser tokenParser;

@Getter
private final String jwksIssuer;

public JwksAwareTokenParser(@NonNull String jwksEndpoint, @NonNull Integer jwksRefreshIntervall, @NonNull String jwksIssuer) {
this.jwksIssuer = jwksIssuer;
LOGGER.info(LogMessages.CONFIGURED_JWKS.format(jwksEndpoint, jwksRefreshIntervall, jwksIssuer));
JWTAuthContextInfo contextInfo = new JWTAuthContextInfo();
contextInfo.setPublicKeyLocation(jwksEndpoint);
contextInfo.setJwksRefreshInterval(jwksRefreshIntervall);
contextInfo.setIssuedBy(jwksIssuer);
LOGGER.debug("Successfully configured JWTAuthContextInfo: %s", contextInfo);
tokenParser = new DefaultJWTParser(contextInfo);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package de.cuioss.portal.authentication.token;

import de.cuioss.tools.logging.LogRecord;
import de.cuioss.tools.logging.LogRecordModel;
import lombok.experimental.UtilityClass;

@UtilityClass
class LogMessages {

static final String PREFIX = "Portal";

// Info-Level
static final LogRecord CONFIGURED_JWKS = LogRecordModel.builder().prefix(PREFIX).identifier(120).template("Initializing JWKS lookup, jwks-endpoint='%s', refresh-interval='%s', issuer = '%s'").build();

// WARN-LEVEL
static final LogRecord TOKEN_IS_EMPTY = LogRecordModel.builder().prefix(PREFIX).identifier(120).template("The given token was empty").build();
static final LogRecord COULD_NOT_PARSE_TOKEN = LogRecordModel.builder().prefix(PREFIX).identifier(121).template("Unable to parse token due to ParseException").build();
static final LogRecord COULD_NOT_PARSE_TOKEN_TRACE = LogRecordModel.builder().prefix(PREFIX).identifier(121).template("Offending token '{}'").build();
}
Loading

0 comments on commit 5f27d22

Please sign in to comment.