Basic scaffolding of the frontend #2
@ -17,3 +17,13 @@ services:
|
|||||||
MOCK_FILE_PATH: /mocks
|
MOCK_FILE_PATH: /mocks
|
||||||
volumes:
|
volumes:
|
||||||
- ./src/mock-api:/mocks
|
- ./src/mock-api:/mocks
|
||||||
|
oauth2:
|
||||||
|
build: https://gitea.dsv.su.se/DMC/oauth2-authorization-server.git
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- '63164:8080'
|
||||||
|
environment:
|
||||||
|
CLIENT_ID: studentportalen
|
||||||
|
CLIENT_SECRET: p4ssw0rd
|
||||||
|
CLIENT_REDIRECT_URI: http://localhost:8080/login/oauth2/code/studentportalen
|
||||||
|
CLIENT_SCOPES: openid profile email offline_access
|
||||||
|
|||||||
@ -25,6 +25,10 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-oauth2-client</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@ -0,0 +1,12 @@
|
|||||||
|
package se.su.dsv.studentportalen.bff;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
@ConfigurationProperties("se.su.dsv.frontend")
|
||||||
|
public record FrontendConfiguration(String url) {
|
||||||
|
public FrontendConfiguration {
|
||||||
|
if (url == null || url.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("se.su.dsv.frontend.url must not be null or blank");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,6 +5,13 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|||||||
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
|
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
|
import se.su.dsv.studentportalen.bff.login.BFFAuthenticationEntryPoint;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableConfigurationProperties
|
@EnableConfigurationProperties
|
||||||
@ -13,4 +20,35 @@ public class Studentportalen extends SpringBootServletInitializer {
|
|||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(Studentportalen.class, args);
|
SpringApplication.run(Studentportalen.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain securityFilterChain(
|
||||||
|
HttpSecurity http,
|
||||||
|
FrontendConfiguration frontendConfiguration)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
http.exceptionHandling(exception -> exception
|
||||||
|
.authenticationEntryPoint(new BFFAuthenticationEntryPoint()));
|
||||||
|
http.oauth2Login(login -> login
|
||||||
|
.defaultSuccessUrl(frontendConfiguration.url(), true));
|
||||||
|
http.authorizeHttpRequests(authorize -> authorize
|
||||||
|
.anyRequest().authenticated());
|
||||||
|
http.cors(cors -> cors
|
||||||
|
.configurationSource(_ -> frontendOnlyCors(frontendConfiguration)));
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CorsConfiguration frontendOnlyCors(FrontendConfiguration frontendConfiguration) {
|
||||||
|
var corsConfiguration = new CorsConfiguration();
|
||||||
|
corsConfiguration.setAllowedOrigins(List.of(frontendConfiguration.url()));
|
||||||
|
corsConfiguration.setAllowedMethods(List.of("GET", "POST"));
|
||||||
|
|
||||||
|
// To allow the session cookie to be included
|
||||||
|
corsConfiguration.setAllowCredentials(true);
|
||||||
|
|
||||||
|
// Content-Type is allowed by default but with a restriction on the value
|
||||||
|
// The restriction does not allow "application/json" so we add it as an allowed header
|
||||||
|
corsConfiguration.setAllowedHeaders(List.of("Content-Type"));
|
||||||
|
return corsConfiguration;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,19 @@
|
|||||||
|
package se.su.dsv.studentportalen.bff.login;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
|
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
||||||
|
|
||||||
|
public class BFFAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||||
|
@Override
|
||||||
|
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) {
|
||||||
|
String loginUri = ServletUriComponentsBuilder.fromRequest(request)
|
||||||
|
.replacePath("/oauth2/authorization/studentportalen")
|
||||||
|
.build()
|
||||||
|
.toUriString();
|
||||||
|
response.addHeader("X-Authorization-Url", loginUri);
|
||||||
|
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
/// This package contains the classes and logic for handling the login process
|
||||||
|
/// described in section 6.1 of [OAuth 2.0 for Browser-Based Applications](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps#name-backend-for-frontend-bff).
|
||||||
|
///
|
||||||
|
/// The frontend running in the browser will attempt to make a request to the
|
||||||
|
/// backend-for-frontend (BFF). If the client is not already authenticated (by
|
||||||
|
/// checking the session cookie), the [se.su.dsv.studentportalen.bff.login.BFFAuthenticationEntryPoint]
|
||||||
|
/// will respond sending a `401 Unauthorized` response with the header
|
||||||
|
/// `X-Authorization-Url` set to where the frontend can begin the authentication
|
||||||
|
/// flow. This initial request corresponds to the request labelled (B) in
|
||||||
|
/// [figure 1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps#figure-1).
|
||||||
|
/// The frontend will then do a full browser navigation to the provided URL (C)
|
||||||
|
/// which triggers the OAuth 2.0 authorization code flow.
|
||||||
|
///
|
||||||
|
/// The entire flow is then performed by Spring Security (D, E, F, and G).
|
||||||
|
/// Once complete, the user is sent back to the frontend (H) with a valid
|
||||||
|
/// session.
|
||||||
|
package se.su.dsv.studentportalen.bff.login;
|
||||||
@ -3,3 +3,18 @@ se:
|
|||||||
dsv:
|
dsv:
|
||||||
backend-api:
|
backend-api:
|
||||||
daisy-url: http://localhost:63163/daisy
|
daisy-url: http://localhost:63163/daisy
|
||||||
|
frontend:
|
||||||
|
url: http://localhost:5173
|
||||||
|
|
||||||
|
spring.security.oauth2.client:
|
||||||
|
provider:
|
||||||
|
dsv:
|
||||||
|
issuer-uri: http://localhost:63164
|
||||||
|
registration:
|
||||||
|
studentportalen:
|
||||||
|
client-id: studentportalen
|
||||||
|
client-secret: p4ssw0rd
|
||||||
|
|
||||||
|
# Lift the restrictions imposed by __Host- prefix during development
|
||||||
|
# Ideally we keep it on, but it breaks in Chromium on Linux
|
||||||
|
server.servlet.session.cookie.name: studentportalen-bff-session
|
||||||
|
|||||||
@ -1,3 +1,29 @@
|
|||||||
|
# The following properties need to be set in each specific environment
|
||||||
|
spring.security.oauth2.client.provider.dsv.issuer-uri: ...
|
||||||
|
spring.security.oauth2.client.registration.studentportalen.client-id: ...
|
||||||
|
spring.security.oauth2.client.registration.studentportalen.client-secret: ...
|
||||||
|
se.su.dsv.backend-api.daisy-url: ...
|
||||||
|
se.su.dsv.frontend.url: ...
|
||||||
|
|
||||||
|
# Common properties for all environments
|
||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: studentportalen-bff
|
name: studentportalen-bff
|
||||||
|
security.oauth2.client:
|
||||||
|
registration:
|
||||||
|
studentportalen:
|
||||||
|
provider: dsv
|
||||||
|
scope:
|
||||||
|
- openid
|
||||||
|
- profile
|
||||||
|
- email
|
||||||
|
- offline_access
|
||||||
|
|
||||||
|
server.servlet.session:
|
||||||
|
cookie:
|
||||||
|
# See https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps#section-6.1.3.2
|
||||||
|
secure: true
|
||||||
|
http-only: true
|
||||||
|
same-site: strict
|
||||||
|
path: /
|
||||||
|
name: __Host-JSESSIONID
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user