Skip to content

Commit

Permalink
Authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
MichiBaum committed Sep 23, 2024
1 parent a286cb1 commit 1ac3183
Show file tree
Hide file tree
Showing 12 changed files with 127 additions and 23 deletions.
11 changes: 11 additions & 0 deletions authentication-service/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,17 @@
<version>1.0.0-TEST-9</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.mockk</groupId>
<artifactId>mockk-jvm</artifactId>
<version>${mockk.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,31 @@ import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import com.michibaum.usermanagement_library.LoginDto
import com.michibaum.authentication_library.PublicKeyDto
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatus
import org.springframework.http.HttpStatusCode
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.ResponseBody

@RestController
@RequiredArgsConstructor
internal class AuthenticationController (
class AuthenticationController (
val authenticationService: AuthenticationService,
val usermanagementClient: UsermanagementClient
) : AuthenticationEndpoints {

@PostMapping(value = ["/authenticate"])
fun authenticate(@RequestBody authenticationDto: AuthenticationDto) {
fun authenticate(@RequestBody authenticationDto: AuthenticationDto): ResponseEntity<Any> {
val loginDto = LoginDto(authenticationDto.username, authenticationDto.password)
val passwordCorrect: Boolean = usermanagementClient.checkPassword(loginDto)
if (passwordCorrect) {
authenticationService.generateJWS()
}

if (!passwordCorrect)
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()

val jws = authenticationService.generateJWS(loginDto.username)
return ResponseEntity.ok().headers { i ->
i.set(HttpHeaders.AUTHORIZATION, jws)
}.build()
}

@get:Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.springframework.stereotype.Service
import java.security.KeyPair
import java.security.interfaces.RSAPrivateKey
import java.security.interfaces.RSAPublicKey
import java.time.Instant

@Service
@RequiredArgsConstructor
Expand All @@ -21,13 +22,16 @@ class AuthenticationService (
keyPair.public.encoded
)

fun generateJWS(): String? {
fun generateJWS(username: String): String? {
val publicKey: RSAPublicKey = keyPair.public as RSAPublicKey
val privateKey: RSAPrivateKey = keyPair.private as RSAPrivateKey
val algorithm = Algorithm.RSA256(publicKey, privateKey)
return JWT.create()
.withHeader(jwsHeaders())
.withIssuer("authentication-service")
.withSubject(username)
.withExpiresAt(Instant.now().plusSeconds(60*60*8))
.withIssuedAt(Instant.now())
.sign(algorithm)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.michibaum.authentication_service.authentication

import com.michibaum.authentication_service.config.UsermanagementClient
import io.mockk.every
import io.mockk.mockk
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.junit.jupiter.MockitoExtension
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatus

@ExtendWith(MockitoExtension::class)
class AuthenticationControllerUT {

private val authenticationService: AuthenticationService = mockk()
private val usermanagementClient: UsermanagementClient = mockk()

private val authenticationController: AuthenticationController = AuthenticationController(authenticationService, usermanagementClient)

@Test
fun `Authentication successful`(){
// GIVEN
every { usermanagementClient.checkPassword(any()) } returns true

val jws = "1234qwer"
val authenticationDto = AuthenticationDto("UName", "Passwört")
every { authenticationService.generateJWS(authenticationDto.username) } returns jws

// WHEN
val result = authenticationController.authenticate(authenticationDto)

// THEN
assertEquals(HttpStatus.OK, result.statusCode)
assertNotNull(result.headers)
assertEquals(jws, result.headers[HttpHeaders.AUTHORIZATION]?.get(0) ?: "")
}

@Test
fun `Bad credentials`(){
// GIVEN
every { usermanagementClient.checkPassword(any()) } returns false
val authenticationDto = AuthenticationDto("UName", "Passwört")

// WHEN
val result = authenticationController.authenticate(authenticationDto)

// THEN
assertEquals(HttpStatus.UNAUTHORIZED, result.statusCode)
assertEquals(0, result.headers.size)
}

}
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<maven.compiler.target>21</maven.compiler.target>
<java.version>21</java.version>
<kotlin.version>1.9.24</kotlin.version>
<mockk.version>1.13.12</mockk.version>

<!-- Docker Hub -->
<dockerHub.url>https://registry.hub.docker.com</dockerHub.url>
Expand Down
8 changes: 7 additions & 1 deletion website/angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,13 @@
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
"sourceMap": true,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.development.ts"
}
]
}
},
"defaultConfiguration": "production"
Expand Down
14 changes: 7 additions & 7 deletions website/src/app/authentication/authentication.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
<div class="p-col-6 p-offset-2">
<div class="p-grid p-dir-col p-justify-center">
<form [formGroup]="loginFormGroup">
<div class="p-col">
<label for="nameField">Benutzername</label>
<input type="text" pInputText formControlName="name" id="nameField" (keydown)="loginOnEnter($event)">
<div class="p-col p-1">
<label for="usernameField">Username</label>
<input type="text" pInputText formControlName="username" id="usernameField">
</div>

<div class="p-col">
<label for="passwordField">Passwort</label>
<input type="password" pPassword formControlName="password" id="passwordField" (keydown)="loginOnEnter($event)"/>
<div class="p-col p-1">
<label for="passwordField">Password</label>
<input type="password" pPassword formControlName="password" id="passwordField"/>
</div>
</form>
<div class="p-col">
<div class="p-col p-1">
<p-button label="login" (onClick)="login()"></p-button>
</div>
</div>
Expand Down
20 changes: 13 additions & 7 deletions website/src/app/authentication/authentication.component.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Component } from '@angular/core';
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms";
import {FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators} from "@angular/forms";
import {Button} from "primeng/button";
import {InputTextModule} from "primeng/inputtext";
import {PasswordModule} from "primeng/password";
import {FloatLabelModule} from "primeng/floatlabel";
import {AuthService} from "../core/services/auth.service";

@Component({
selector: 'app-authentication',
Expand All @@ -11,7 +13,9 @@ import {PasswordModule} from "primeng/password";
ReactiveFormsModule,
Button,
InputTextModule,
PasswordModule
PasswordModule,
FloatLabelModule,
FormsModule
],
templateUrl: './authentication.component.html',
styleUrl: './authentication.component.scss'
Expand All @@ -20,7 +24,7 @@ export class AuthenticationComponent {

loginFormGroup = new FormGroup(
{
name: new FormControl('', [
username: new FormControl('', [
Validators.required
]),
password: new FormControl('', [
Expand All @@ -29,12 +33,14 @@ export class AuthenticationComponent {
}
);

login(){

constructor(private authService: AuthService) {
}

loginOnEnter(event: KeyboardEvent){

login(){
if(!this.loginFormGroup.valid)
return;
var values = this.loginFormGroup.value
this.authService.login(values.username ?? '', values.password ?? '')
}

}
4 changes: 4 additions & 0 deletions website/src/app/core/models/authentication.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface Authentication {
username: string;
password: string;
}
6 changes: 4 additions & 2 deletions website/src/app/core/services/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {inject, Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {CookieService} from "ngx-cookie-service";
import {ActivatedRouteSnapshot, Router} from "@angular/router";
import {Authentication} from "../models/authentication.model";

@Injectable({providedIn: 'root'})
export class AuthService {
Expand All @@ -11,8 +12,9 @@ export class AuthService {
constructor(private http: HttpClient, private router:Router) {
}

login(email:string, password:string ) {

login(username:string, password:string ) {
let autenticationModel = {username: username, password: password} as Authentication
this.http.post('/authenticate', autenticationModel).subscribe(value => console.log(value))
}

getJwtTokenFromCookie(){
Expand Down
3 changes: 3 additions & 0 deletions website/src/environments/environment.development.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const environment = {
url: 'http:michibaum.ch'
};
3 changes: 3 additions & 0 deletions website/src/environments/environment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const environment = {
url: 'http:michibaum.ch'
};

0 comments on commit 1ac3183

Please sign in to comment.