Skip to content

Commit

Permalink
Adding TokenFactory
Browse files Browse the repository at this point in the history
  • Loading branch information
cuioss committed Jan 22, 2025
1 parent dc0364d commit 8af6f07
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,18 @@ public OffsetDateTime getExpirationTime() {
return OffsetDateTime
.ofInstant(Instant.ofEpochSecond(jsonWebToken.getExpirationTime()), ZoneId.systemDefault());
}

/**
* @return the subject from the underlying JWT token
*/
public String getSubject() {
return jsonWebToken.getSubject();
}

/**
* @return the issuer from the underlying JWT token
*/
public String getIssuer() {
return jsonWebToken.getIssuer();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package de.cuioss.portal.authentication.token;

import io.smallrye.jwt.auth.principal.JWTParser;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

/**
* Factory for creating different types of tokens.
*/
@ApplicationScoped
@RequiredArgsConstructor(access = AccessLevel.PRIVATE, onConstructor_ = @Inject)
public class TokenFactory {

private final JWTParser tokenParser;

/**
* Creates a new token factory using the given parser.
*
* @param tokenParser The parser to use for token validation, must not be null
* @return A new TokenFactory instance
*/
public static TokenFactory of(@NonNull JWTParser tokenParser) {
return new TokenFactory(tokenParser);
}

/**
* Creates an access token from the given token string.
*
* @param tokenString The token string to parse, must not be null
* @return The parsed access token
*/
public ParsedAccessToken createAccessToken(@NonNull String tokenString) {
return ParsedAccessToken.fromTokenString(tokenString, tokenParser);
}

/**
* Creates an ID token from the given token string.
*
* @param tokenString The token string to parse, must not be null
* @return The parsed ID token
*/
public ParsedIdToken createIdToken(@NonNull String tokenString) {
return ParsedIdToken.fromTokenString(tokenString, tokenParser);
}

/**
* Creates a refresh token from the given token string.
*
* @param tokenString The token string to parse, must not be null
* @return The parsed refresh token
*/
public ParsedRefreshToken createRefreshToken(@NonNull String tokenString) {
return ParsedRefreshToken.fromTokenString(tokenString);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@
import de.cuioss.test.juli.TestLogLevel;
import de.cuioss.test.juli.junit5.EnableTestLogger;
import de.cuioss.tools.logging.CuiLogger;
import io.smallrye.jwt.auth.principal.DefaultJWTParser;
import org.eclipse.microprofile.jwt.JsonWebToken;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.NullAndEmptySource;
import org.junit.jupiter.params.provider.ValueSource;

import static de.cuioss.portal.authentication.token.TestTokenProducer.*;
import static de.cuioss.portal.authentication.token.TestTokenProducer.DEFAULT_TOKEN_PARSER;
import static de.cuioss.portal.authentication.token.TestTokenProducer.SOME_SCOPES;
import static de.cuioss.portal.authentication.token.TestTokenProducer.validSignedJWTWithClaims;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

Expand All @@ -41,42 +42,48 @@ class ParsedTokenTest {
@NullAndEmptySource
@ValueSource(strings = " ")
void shouldProvideEmptyFallbackOnEmptyInput(String initialTokenString) {
JsonWebToken jsonWebToken = ParsedToken.jsonWebTokenFrom(initialTokenString, TestTokenProducer.DEFAULT_TOKEN_PARSER, LOGGER);
JsonWebToken jsonWebToken = ParsedToken.jsonWebTokenFrom(initialTokenString,
TestTokenProducer.DEFAULT_TOKEN_PARSER, LOGGER);

Assertions.assertEquals(ParsedToken.EMPTY_WEB_TOKEN, jsonWebToken);
LogAsserts.assertSingleLogMessagePresentContaining(TestLogLevel.WARN, LogMessages.TOKEN_IS_EMPTY.resolveIdentifierString());
LogAsserts.assertSingleLogMessagePresentContaining(TestLogLevel.WARN,
LogMessages.TOKEN_IS_EMPTY.resolveIdentifierString());
}

@Test
void shouldProvideEmptyFallbackOnParseError() {
String initialTokenString = Generators.letterStrings(10, 20).next();

JsonWebToken jsonWebToken = ParsedToken.jsonWebTokenFrom(initialTokenString, TestTokenProducer.DEFAULT_TOKEN_PARSER, LOGGER);
JsonWebToken jsonWebToken = ParsedToken.jsonWebTokenFrom(initialTokenString,
TestTokenProducer.DEFAULT_TOKEN_PARSER, LOGGER);

Assertions.assertEquals(ParsedToken.EMPTY_WEB_TOKEN, jsonWebToken);
LogAsserts.assertSingleLogMessagePresentContaining(TestLogLevel.WARN, LogMessages.COULD_NOT_PARSE_TOKEN.resolveIdentifierString());
LogAsserts.assertSingleLogMessagePresentContaining(TestLogLevel.WARN,
LogMessages.COULD_NOT_PARSE_TOKEN.resolveIdentifierString());
}

@Test
void shouldProvideEmptyFallbackOnInvalidIssuer() {
String initialTokenString = TestTokenProducer.validSignedJWTWithClaims(TestTokenProducer.SOME_SCOPES);

JsonWebToken jsonWebToken = ParsedToken
.jsonWebTokenFrom(initialTokenString, new DefaultJWTParser(TestTokenProducer.TEST_AUTH_CONTEXT_INFO_WRONG_ISSUER), LOGGER);
.jsonWebTokenFrom(initialTokenString, TestTokenProducer.WRONG_ISSUER_TOKEN_PARSER, LOGGER);

Assertions.assertEquals(ParsedToken.EMPTY_WEB_TOKEN, jsonWebToken);
LogAsserts.assertSingleLogMessagePresentContaining(TestLogLevel.WARN, LogMessages.COULD_NOT_PARSE_TOKEN.resolveIdentifierString());
LogAsserts.assertSingleLogMessagePresentContaining(TestLogLevel.WARN,
LogMessages.COULD_NOT_PARSE_TOKEN.resolveIdentifierString());
}

@Test
void shouldProvideEmptyFallbackOnWrongPublicKey() {
String initialTokenString = TestTokenProducer.validSignedJWTWithClaims(TestTokenProducer.SOME_SCOPES);

JsonWebToken jsonWebToken = ParsedToken
.jsonWebTokenFrom(initialTokenString, new DefaultJWTParser(TestTokenProducer.TEST_AUTH_CONTEXT_INFO_WRONG_PUBLIC_KEY),
LOGGER);
.jsonWebTokenFrom(initialTokenString,
TestTokenProducer.WRONG_SIGNATURE_TOKEN_PARSER, LOGGER);
Assertions.assertEquals(ParsedToken.EMPTY_WEB_TOKEN, jsonWebToken);
LogAsserts.assertSingleLogMessagePresentContaining(TestLogLevel.WARN, LogMessages.COULD_NOT_PARSE_TOKEN.resolveIdentifierString());
LogAsserts.assertSingleLogMessagePresentContaining(TestLogLevel.WARN,
LogMessages.COULD_NOT_PARSE_TOKEN.resolveIdentifierString());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,21 @@ public class TestTokenProducer {

public static final String SOME_ID_TOKEN = BASE_PATH + "some-id-token.json";


public static final JWTAuthContextInfo TEST_AUTH_CONTEXT_INFO = new JWTAuthContextInfo(PUBLIC_KEY, ISSUER);

public static final JWTAuthContextInfo TEST_AUTH_CONTEXT_INFO_WRONG_PUBLIC_KEY =
new JWTAuthContextInfo(PUBLIC_KEY_OTHER, ISSUER);
public static final JWTAuthContextInfo TEST_AUTH_CONTEXT_INFO_WRONG_PUBLIC_KEY = new JWTAuthContextInfo(
PUBLIC_KEY_OTHER, ISSUER);

public static final JWTAuthContextInfo TEST_AUTH_CONTEXT_INFO_WRONG_ISSUER =
new JWTAuthContextInfo(PUBLIC_KEY, new StringBuilder(ISSUER).reverse().toString());
public static final JWTAuthContextInfo TEST_AUTH_CONTEXT_INFO_WRONG_ISSUER = new JWTAuthContextInfo(PUBLIC_KEY,
new StringBuilder(ISSUER).reverse().toString());

public static final JWTParser DEFAULT_TOKEN_PARSER = new DefaultJWTParser(TEST_AUTH_CONTEXT_INFO);

public static final JWTParser WRONG_ISSUER_TOKEN_PARSER = new DefaultJWTParser(TEST_AUTH_CONTEXT_INFO_WRONG_ISSUER);

public static final JWTParser WRONG_SIGNATURE_TOKEN_PARSER = new DefaultJWTParser(
TEST_AUTH_CONTEXT_INFO_WRONG_PUBLIC_KEY);

public static final String SUBJECT = Generators.letterStrings(10, 12).next();

public static String validSignedJWTWithClaims(String claims) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package de.cuioss.portal.authentication.token;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.time.Instant;
import java.time.temporal.ChronoUnit;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

class TokenFactoryTest {

private TokenFactory tokenFactory;

@BeforeEach
void setUp() {
tokenFactory = TokenFactory.of(TestTokenProducer.DEFAULT_TOKEN_PARSER);
}

@Test
void shouldCreateAccessToken() {
var token = TestTokenProducer.validSignedJWTWithClaims(TestTokenProducer.SOME_SCOPES);
var parsedToken = tokenFactory.createAccessToken(token);

assertNotNull(parsedToken);
assertFalse(parsedToken.getScopes().isEmpty());
assertNotNull(parsedToken.getSubject());
assertNotNull(parsedToken.getIssuer());
}

@Test
void shouldCreateIdToken() {
var token = TestTokenProducer.validSignedJWTWithClaims(TestTokenProducer.SOME_ID_TOKEN);
var parsedToken = tokenFactory.createIdToken(token);

assertNotNull(parsedToken);
assertNotNull(parsedToken.getSubject());
assertNotNull(parsedToken.getIssuer());
}

@Test
void shouldCreateRefreshToken() {
var token = TestTokenProducer.validSignedJWTWithClaims(TestTokenProducer.REFRESH_TOKEN);
var parsedToken = tokenFactory.createRefreshToken(token);

assertNotNull(parsedToken);
}

@Test
void shouldHandleExpiredToken() {
var expiredToken = TestTokenProducer.validSignedJWTExpireAt(
Instant.now().minus(1, ChronoUnit.HOURS));
var token = tokenFactory.createAccessToken(expiredToken);

assertNotNull(token);
assertTrue(token.isEmpty());
}

@Test
void shouldHandleInvalidIssuer() {
var wrongIssuerTokenFactory = TokenFactory.of(TestTokenProducer.WRONG_ISSUER_TOKEN_PARSER);
var token = TestTokenProducer.validSignedJWTWithClaims(TestTokenProducer.SOME_SCOPES);
var parsedToken = wrongIssuerTokenFactory.createAccessToken(token);
assertNotNull(parsedToken);
assertTrue(parsedToken.isEmpty());
}

@Test
void shouldHandleInvalidSignature() {
var wrongSignatureTokenFactory = TokenFactory.of(TestTokenProducer.WRONG_SIGNATURE_TOKEN_PARSER);
var token = TestTokenProducer.validSignedJWTWithClaims(TestTokenProducer.SOME_SCOPES);
var parsedToken = wrongSignatureTokenFactory.createAccessToken(token);
assertNotNull(parsedToken);
assertTrue(parsedToken.isEmpty());
}
}

0 comments on commit 8af6f07

Please sign in to comment.