Skip to content

Commit

Permalink
- Added functionality to redirect to the home page when clicking the …
Browse files Browse the repository at this point in the history
…logo in the login page.

- Added a timeout that redirects to the home page when the QR code expires in the login page.
  • Loading branch information
rubenmodamioin2 committed Oct 30, 2024
1 parent f70da36 commit 49c9397
Show file tree
Hide file tree
Showing 17 changed files with 170 additions and 45 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v1.0.2]
### Fixed
- Added functionality to redirect to the home page when clicking the logo in the login page.
- Added a timeout that redirects to the home page when the QR code expires in the login page.

## [v1.0.1]
### Fixed
- Fixed registration button link on the login qr page.
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ plugins {
}

group = 'es.in2'
version = '1.0.1'
version = '1.0.2'

java {
toolchain {
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/es/in2/vcverifier/config/CacheStoreConfig.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package es.in2.vcverifier.config;

import es.in2.vcverifier.config.properties.SecurityProperties;
import es.in2.vcverifier.model.AuthorizationCodeData;
import es.in2.vcverifier.model.AuthorizationRequestJWT;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;

import java.time.temporal.ChronoUnit;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
Expand All @@ -15,9 +17,12 @@
@RequiredArgsConstructor
public class CacheStoreConfig {

private final SecurityProperties securityProperties;
@Bean
public CacheStore<AuthorizationRequestJWT> cacheStoreForAuthorizationRequestJWT() {
return new CacheStore<>(10, TimeUnit.MINUTES);
return new CacheStore<>(
Long.parseLong(securityProperties.loginCode().expirationProperties().expiration()),
TimeUnit.of(ChronoUnit.valueOf(securityProperties.token().accessToken().cronUnit())));
}

@Bean
Expand Down
15 changes: 10 additions & 5 deletions src/main/java/es/in2/vcverifier/config/ClientLoaderConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,15 @@ private List<RegisteredClient> loadClients() {
List<RegisteredClient> registeredClients = new ArrayList<>();
// Convertir cada ClientData a RegisteredClient y agregarlo a la lista
for (ClientData clientData : clientsYamlData.clients()) {
RegisteredClient.Builder registeredClientBuilder = RegisteredClient.withId(UUID.randomUUID().toString()).clientId(clientData.clientId()).clientAuthenticationMethods(authMethods -> clientData.clientAuthenticationMethods().forEach(method -> authMethods.add(new ClientAuthenticationMethod(method)))).authorizationGrantTypes(grantTypes -> clientData.authorizationGrantTypes().forEach(grantType -> grantTypes.add(new AuthorizationGrantType(grantType)))).redirectUris(uris -> uris.addAll(clientData.redirectUris())).postLogoutRedirectUris(uris -> uris.addAll(clientData.postLogoutRedirectUris())).scopes(scopes -> scopes.addAll(clientData.scopes()));
RegisteredClient.Builder registeredClientBuilder = RegisteredClient
.withId(UUID.randomUUID().toString())
.clientId(clientData.clientId())
.clientAuthenticationMethods(authMethods -> clientData.clientAuthenticationMethods().forEach(method -> authMethods.add(new ClientAuthenticationMethod(method))))
.authorizationGrantTypes(grantTypes -> clientData.authorizationGrantTypes().forEach(grantType -> grantTypes.add(new AuthorizationGrantType(grantType))))
.redirectUris(uris -> uris.addAll(clientData.redirectUris()))
.postLogoutRedirectUris(uris -> uris.addAll(clientData.postLogoutRedirectUris()))
.scopes(scopes -> scopes.addAll(clientData.scopes()))
.clientName(clientData.domain());

if (clientData.clientSecret() != null && !clientData.clientSecret().isBlank()) {
registeredClientBuilder.clientSecret(clientData.clientSecret());
Expand All @@ -67,7 +75,4 @@ private List<RegisteredClient> loadClients() {
throw new ClientLoadingException("Error loading clients from Yaml", e);
}
}

}


}
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,36 @@
import java.util.Optional;

@ConfigurationProperties(prefix = "security")
public record SecurityProperties(String authorizationServer, @NestedConfigurationProperty TokenProperties token) {
public record SecurityProperties(String authorizationServer, @NestedConfigurationProperty TokenProperties token,
@NestedConfigurationProperty LoginCodeProperties loginCode) {

@ConstructorBinding
public SecurityProperties(String authorizationServer, TokenProperties token) {
public SecurityProperties(String authorizationServer, TokenProperties token, LoginCodeProperties loginCode) {
this.authorizationServer = authorizationServer;
this.token = Optional.ofNullable(token).orElse(new TokenProperties(null));
this.loginCode = Optional.ofNullable(loginCode).orElse(new LoginCodeProperties(null));
}

public record TokenProperties(@NestedConfigurationProperty AccessTokenProperties accessToken) {

@ConstructorBinding
public TokenProperties(AccessTokenProperties accessToken) {
this.accessToken = Optional.ofNullable(accessToken).orElse(new AccessTokenProperties(null, null));
this.accessToken = Optional.ofNullable(accessToken).orElse(new AccessTokenProperties("30", "MINUTES"));
}

public record AccessTokenProperties(String expiration, String cronUnit) {
}

}

public record LoginCodeProperties(@NestedConfigurationProperty ExpirationProperties expirationProperties) {

@ConstructorBinding
public LoginCodeProperties(ExpirationProperties expirationProperties) {
this.expirationProperties = Optional.ofNullable(expirationProperties).orElse(new ExpirationProperties("5", "MINUTES"));
}

public record ExpirationProperties(String expiration, String cronUnit) {
}
}
}

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package es.in2.vcverifier.controller;

import es.in2.vcverifier.config.properties.SecurityProperties;
import es.in2.vcverifier.config.properties.VerifierUiLoginUrisProperties;
import es.in2.vcverifier.exception.QRCodeGenerationException;
import lombok.RequiredArgsConstructor;
Expand All @@ -19,10 +20,11 @@
public class LoginQrController {

private final VerifierUiLoginUrisProperties verifierUiLoginUrisProperties;
private final SecurityProperties securityProperties;

@GetMapping("/login")
@ResponseStatus(HttpStatus.OK)
public String showQrLogin(@RequestParam("authRequest") String authRequest, @RequestParam("state") String state, Model model) {
public String showQrLogin(@RequestParam("authRequest") String authRequest, @RequestParam("state") String state, Model model, @RequestParam("homeUri") String homeUri) {
try {
// Generar la imagen QR en base64
String qrImageBase64 = generateQRCodeImageBase64(authRequest);
Expand All @@ -31,9 +33,12 @@ public String showQrLogin(@RequestParam("authRequest") String authRequest, @Requ
model.addAttribute("authRequest", authRequest);
// Pasar el sessionId al modelo
model.addAttribute("state", state);
model.addAttribute("homeUri", homeUri);
model.addAttribute("onboardingUri", verifierUiLoginUrisProperties.onboardingUri());
model.addAttribute("supportUri", verifierUiLoginUrisProperties.supportUri());
model.addAttribute("walletUri", verifierUiLoginUrisProperties.walletUri());
model.addAttribute("cronUnit", securityProperties.loginCode().expirationProperties().cronUnit());
model.addAttribute("expiration", securityProperties.loginCode().expirationProperties().expiration());
} catch (Exception e) {
throw new QRCodeGenerationException(e.getMessage());
}
Expand Down
1 change: 1 addition & 0 deletions src/main/java/es/in2/vcverifier/model/ClientData.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

public record ClientData(
String id,
String domain,
String clientId,
String clientSecret,
List<String> redirectUris,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity h
// Adds an AuthenticationConverter (pre-processor) used when attempting to extract
// an OAuth2 authorization request (or consent) from HttpServletRequest to an instance
// of OAuth2AuthorizationCodeRequestAuthenticationToken or OAuth2AuthorizationConsentAuthenticationToken.
.authorizationRequestConverter(new CustomAuthorizationRequestConverter(didService,jwtService,cryptoComponent,cacheStoreForAuthorizationRequestJWT,cacheStoreForOAuth2AuthorizationRequest,securityProperties))
.authorizationRequestConverter(new CustomAuthorizationRequestConverter(didService,jwtService,cryptoComponent,cacheStoreForAuthorizationRequestJWT,cacheStoreForOAuth2AuthorizationRequest,securityProperties,registeredClientRepository))
.errorResponseHandler(new CustomErrorResponseHandler())
)
.tokenEndpoint(tokenEndpoint ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import com.nimbusds.jose.JWSObject;
import com.nimbusds.jwt.JWTClaimsSet;
import es.in2.vcverifier.component.CryptoComponent;
import es.in2.vcverifier.config.CacheStore;
import es.in2.vcverifier.config.properties.SecurityProperties;
import es.in2.vcverifier.component.CryptoComponent;
import es.in2.vcverifier.exception.JWTParsingException;
import es.in2.vcverifier.exception.RequestMismatchException;
import es.in2.vcverifier.exception.RequestObjectRetrievalException;
Expand All @@ -19,9 +19,13 @@
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationException;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.web.authentication.AuthenticationConverter;

import java.io.IOException;
Expand Down Expand Up @@ -50,6 +54,7 @@ public class CustomAuthorizationRequestConverter implements AuthenticationConver
private final CacheStore<AuthorizationRequestJWT> cacheStoreForAuthorizationRequestJWT;
private final CacheStore<OAuth2AuthorizationRequest> cacheStoreForOAuth2AuthorizationRequest;
private final SecurityProperties securityProperties;
private final RegisteredClientRepository registeredClientRepository;

/**
* The Authorization Request MUST be signed by the Client, and MUST use the request_uri parameter which enables
Expand All @@ -68,9 +73,17 @@ public Authentication convert(HttpServletRequest request) {
String clientId = request.getParameter(CLIENT_ID); // client_id parameter
String state = request.getParameter("state");
String scope = request.getParameter(SCOPE);

RegisteredClient registeredClient = registeredClientRepository.findByClientId(clientId);

if (registeredClient == null) {
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);
}

if (clientId == null) {
throw new IllegalArgumentException("Client ID is required.");
}

// Case 1: JWT needs to be retrieved via "request_uri"
if (requestUri != null) {
log.info("Retrieving JWT from request_uri: " + requestUri);
Expand Down Expand Up @@ -117,10 +130,15 @@ public Authentication convert(HttpServletRequest request) {
.authRequest(signedAuthRequest)
.build()
);

// This is used to allow the user te return to the application if the user wants to cancel the login
String homeUri = registeredClient.getClientName();

String authRequest = generateOpenId4VpUrl(nonce);
String redirectUrl = String.format("/login?authRequest=%s&state=%s",
String redirectUrl = String.format("/login?authRequest=%s&state=%s&homeUri=%s",
URLEncoder.encode(authRequest, StandardCharsets.UTF_8),
URLEncoder.encode(state, StandardCharsets.UTF_8));
URLEncoder.encode(state, StandardCharsets.UTF_8),
URLEncoder.encode(homeUri, StandardCharsets.UTF_8));
OAuth2Error error = new OAuth2Error("custom_error", "Redirection required", redirectUrl);
throw new OAuth2AuthorizationCodeRequestAuthenticationException(error,null);
} catch (ParseException e) {
Expand All @@ -138,6 +156,7 @@ private boolean validateOAuth2Parameters(HttpServletRequest request, JWSObject j
String jwtResponseType = jwtClaims.optString(RESPONSE_TYPE);
String jwtClientId = jwtClaims.optString(CLIENT_ID);
String jwtScope = jwtClaims.optString(SCOPE);

// Ensure that required OAuth 2.0 parameters match those in the JWT
return requestResponseType.equals(jwtResponseType)
&& requestClientId.equals(jwtClientId)
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/application-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ management:

security:
authorizationServer:
loginCode:
expirationProperties:
expiration:
cronUnit:
token:
accessToken:
cronUnit:
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/application-local.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ management:

security:
authorizationServer: "https://verifier.dome-marketplace-lcl.org"
loginCode:
expirationProperties:
expiration: "5"
cronUnit: "MINUTES"
token:
accessToken:
expiration: "1"
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/application-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ management:

security:
authorizationServer:
loginCode:
expirationProperties:
expiration:
cronUnit:
token:
accessToken:
cronUnit:
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/application-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ management:

security:
authorizationServer:
loginCode:
expirationProperties:
expiration:
cronUnit:
token:
accessToken:
cronUnit:
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ management:

security:
authorizationServer: "https://verifier.dome-marketplace-lcl.org"
loginCode:
expirationProperties:
expiration: "5"
cronUnit: "MINUTES"
token:
accessToken:
expiration: "5"
Expand Down
Loading

0 comments on commit 49c9397

Please sign in to comment.