feat(fullstack): Add JWT validation

This commit is contained in:
2025-04-26 17:39:20 +02:00
parent 28954f7c2e
commit 667aab1f36
7 changed files with 84 additions and 5 deletions

View File

@@ -8,7 +8,10 @@ info:
email: denis.lugowski@gmail.com
servers:
- url: /
- url: http://localhost:8080
security:
- bearerAuth: []
paths:
/application-forms:
@@ -592,6 +595,12 @@ paths:
$ref: "https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/ServiceUnavailable"
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
schemas:
####### UserDto #######
UserDto:

View File

@@ -24,9 +24,12 @@ dependencies {
implementation 'com.fasterxml.jackson.module:jackson-module-kotlin'
implementation 'org.jetbrains.kotlin:kotlin-reflect'
implementation 'org.liquibase:liquibase-core'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.4'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.6'
implementation 'org.springdoc:springdoc-openapi-ui:1.8.0'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// https://docs.spring.io/spring-security/reference/servlet/oauth2/index.html#oauth2-resource-server-access-token-jwt
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
runtimeOnly 'com.h2database:h2'
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5'

View File

@@ -0,0 +1,39 @@
package com.betriebsratkanzlei.legalconsenthub.config
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.invoke
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm
import org.springframework.security.oauth2.jwt.JwtDecoder
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder
import org.springframework.security.web.SecurityFilterChain
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
csrf { disable() }
authorizeHttpRequests {
authorize("/swagger-ui/**", permitAll)
authorize("/v3/**", permitAll)
authorize(anyRequest, authenticated)
}
oauth2ResourceServer {
jwt { }
}
}
return http.build()
}
@Bean
fun jwtDecoder(): JwtDecoder {
return NimbusJwtDecoder.withJwkSetUri("http://192.168.178.105:3001/api/auth/jwks")
.jwsAlgorithm(SignatureAlgorithm.ES512).build()
}
}

View File

@@ -22,6 +22,14 @@ spring:
order_inserts: true
enable_lazy_load_no_trans: true
# security:
# oauth2:
# resourceserver:
# jwt:
# issuer-uri: http://192.168.178.105:3001
# jwk-set-uri: http://192.168.178.105:3001/api/auth/jwks
# jws-algorithms: ES512
liquibase:
enabled: true
drop-first: false
@@ -31,3 +39,10 @@ spring:
sql:
init:
platform: h2
logging:
level:
org:
springframework:
security: TRACE
oauth2: TRACE

View File

@@ -12,7 +12,7 @@ export function useApplicationFormApi() {
)
const applicationFormApiClient = new ApplicationFormApi(
new Configuration({ basePath, headers: { Authorization: jwt.value ?? '' } })
new Configuration({ basePath, headers: { Authorization: jwt.value ? `Bearer ${jwt.value}` : '' } })
)
async function createApplicationForm(

View File

@@ -5,12 +5,15 @@ import { cleanDoubleSlashes, withoutTrailingSlash } from 'ufo'
export function useApplicationFormTemplateApi() {
const appBaseUrl = useRuntimeConfig().app.baseURL
const { serverApiBaseUrl, serverApiBasePath, clientProxyBasePath } = useRuntimeConfig().public
const { jwt } = useAuth()
const basePath = withoutTrailingSlash(
cleanDoubleSlashes(import.meta.client ? appBaseUrl + clientProxyBasePath : serverApiBaseUrl + serverApiBasePath)
)
const applicationFormApiClient = new ApplicationFormTemplateApi(new Configuration({ basePath }))
const applicationFormApiClient = new ApplicationFormTemplateApi(
new Configuration({ basePath, headers: { Authorization: jwt.value ? `Bearer ${jwt.value}` : '' } })
)
async function createApplicationFormTemplate(
createApplicationFormDto: CreateApplicationFormDto

View File

@@ -7,7 +7,17 @@ export const auth = betterAuth({
database: new Database('./sqlite.db'),
emailAndPassword: { enabled: true, autoSignIn: false },
plugins: [
jwt(),
jwt({
jwt: {
issuer: 'http://192.168.178.105:3001'
},
jwks: {
keyPairConfig: {
// Supported by NimbusJwtDecoder
alg: 'ES512'
}
}
}),
organization({
async sendInvitationEmail(data) {
console.log('Sending invitation email', data)