Compare commits
10 Commits
2359c9c5e9
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd216edf50 | ||
|
|
990d268460 | ||
|
|
fe27c6f918 | ||
|
|
afed523d5b | ||
|
|
40cd25a771 | ||
|
|
3bff98503e | ||
|
|
f9b151d914 | ||
|
|
abd623f2ca | ||
|
|
ec29f8d4b1 | ||
|
|
2362f04704 |
@@ -23,5 +23,6 @@ MAILER_PASSWORD=xxxxx
|
|||||||
MAILER_FROM='"No Reply" <noreply@example.com>'
|
MAILER_FROM='"No Reply" <noreply@example.com>'
|
||||||
|
|
||||||
# Client
|
# Client
|
||||||
|
# Url wird für Mailversand gebraucht
|
||||||
CLIENT_URL=http://localhost:4200
|
CLIENT_URL=http://localhost:4200
|
||||||
PRODUCTION=true
|
PRODUCTION=true
|
||||||
@@ -12,7 +12,7 @@ export class ApplicationController {
|
|||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('login')
|
@Post('authorize')
|
||||||
loginUser(@Body() b: LoginUserDto): Promise<User> {
|
loginUser(@Body() b: LoginUserDto): Promise<User> {
|
||||||
return this.userService.loginUser({
|
return this.userService.loginUser({
|
||||||
username: b.username,
|
username: b.username,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { Client } from 'src/model';
|
|||||||
import { UsersService } from 'src/shared/users.service';
|
import { UsersService } from 'src/shared/users.service';
|
||||||
import { CustomLogger } from 'src/shared/logger/custom.logger';
|
import { CustomLogger } from 'src/shared/logger/custom.logger';
|
||||||
|
|
||||||
@Controller('auth')
|
@Controller('')
|
||||||
export class AuthController {
|
export class AuthController {
|
||||||
constructor(
|
constructor(
|
||||||
private usersService: UsersService,
|
private usersService: UsersService,
|
||||||
@@ -30,7 +30,7 @@ export class AuthController {
|
|||||||
async login(
|
async login(
|
||||||
@Body('username') username: string,
|
@Body('username') username: string,
|
||||||
@Body('password') password: string,
|
@Body('password') password: string,
|
||||||
@Body('client_id') clientId: string,
|
@Query('client_id') clientId: string,
|
||||||
) {
|
) {
|
||||||
const token = await this.usersService.createToken(
|
const token = await this.usersService.createToken(
|
||||||
username,
|
username,
|
||||||
@@ -57,7 +57,7 @@ export class AuthController {
|
|||||||
return this.usersService.loginWithSessionKey(code, null, true);
|
return this.usersService.loginWithSessionKey(code, null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get('client')
|
||||||
async getClient(
|
async getClient(
|
||||||
@Query('client_id') clientId: string,
|
@Query('client_id') clientId: string,
|
||||||
@Query('response_type') responseType: string,
|
@Query('response_type') responseType: string,
|
||||||
@@ -72,7 +72,7 @@ export class AuthController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('token')
|
@Post('authorize')
|
||||||
@FormDataRequest()
|
@FormDataRequest()
|
||||||
async getToken(
|
async getToken(
|
||||||
@Body('client_id') clientId: string,
|
@Body('client_id') clientId: string,
|
||||||
|
|||||||
@@ -5,9 +5,12 @@ import {
|
|||||||
DataSource,
|
DataSource,
|
||||||
Repository,
|
Repository,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
|
Column,
|
||||||
|
BeforeInsert,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
import { User } from './user.entity';
|
import { User } from './user.entity';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { Exclude } from 'class-transformer';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class SessionKey {
|
export class SessionKey {
|
||||||
@@ -17,8 +20,17 @@ export class SessionKey {
|
|||||||
@ManyToOne(() => User, (user) => user.sessionKeys, { eager: true })
|
@ManyToOne(() => User, (user) => user.sessionKeys, { eager: true })
|
||||||
user: User;
|
user: User;
|
||||||
|
|
||||||
|
@Exclude()
|
||||||
|
@Column()
|
||||||
|
pwRevision?: number;
|
||||||
|
|
||||||
@CreateDateColumn()
|
@CreateDateColumn()
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
|
|
||||||
|
@BeforeInsert()
|
||||||
|
setPWRevision() {
|
||||||
|
this.pwRevision = this.user.pwRevision;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ export class User {
|
|||||||
@ManyToMany(() => Client, (client) => client.admins)
|
@ManyToMany(() => Client, (client) => client.admins)
|
||||||
clients?: Client[];
|
clients?: Client[];
|
||||||
|
|
||||||
|
@Exclude()
|
||||||
|
@Column({ type: 'int', default: 0 })
|
||||||
|
pwRevision?: number; // wird hochgezählt wenn das PW geändert wird. somit kann der Token invalid gesetzt werden.
|
||||||
|
|
||||||
accessToken?: string;
|
accessToken?: string;
|
||||||
refreshToken?: string;
|
refreshToken?: string;
|
||||||
session_key?: string;
|
session_key?: string;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
export * from './authenticated.request';
|
export * from './authenticated.request';
|
||||||
export * from './logger.interface';
|
export * from './logger.interface';
|
||||||
export * from './mailconfig.interface';
|
export * from './mailconfig.interface';
|
||||||
|
export * from './payload.interface';
|
||||||
|
|||||||
18
idp/src/model/interface/payload.interface.ts
Normal file
18
idp/src/model/interface/payload.interface.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
export interface IAccessPayload {
|
||||||
|
username: string;
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
id: string;
|
||||||
|
iss: string;
|
||||||
|
aud: string;
|
||||||
|
iat: number;
|
||||||
|
exp: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRefreshPayload {
|
||||||
|
type: string;
|
||||||
|
id: string;
|
||||||
|
token_revision: number;
|
||||||
|
iat: number;
|
||||||
|
exp: number;
|
||||||
|
}
|
||||||
@@ -15,6 +15,8 @@ import {
|
|||||||
AuthorizationCodeRepository,
|
AuthorizationCodeRepository,
|
||||||
AuthorizationCode,
|
AuthorizationCode,
|
||||||
ActivityLogRepository,
|
ActivityLogRepository,
|
||||||
|
IAccessPayload,
|
||||||
|
IRefreshPayload,
|
||||||
} from 'src/model';
|
} from 'src/model';
|
||||||
import { CustomLogger } from './logger/custom.logger';
|
import { CustomLogger } from './logger/custom.logger';
|
||||||
|
|
||||||
@@ -123,6 +125,10 @@ export class UsersService {
|
|||||||
throw new HttpException('User is not active', 401);
|
throw new HttpException('User is not active', 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (user.pwRevision != session.pwRevision) {
|
||||||
|
throw new HttpException('Invalid session key', 401);
|
||||||
|
}
|
||||||
|
|
||||||
if (getUserAccessToken) {
|
if (getUserAccessToken) {
|
||||||
user.accessToken = this.createAccessToken(user);
|
user.accessToken = this.createAccessToken(user);
|
||||||
user.refreshToken = this.createRefreshToken(user);
|
user.refreshToken = this.createRefreshToken(user);
|
||||||
@@ -205,25 +211,30 @@ export class UsersService {
|
|||||||
{
|
{
|
||||||
type: 'refresh',
|
type: 'refresh',
|
||||||
id: user.id,
|
id: user.id,
|
||||||
|
token_revision: user.pwRevision,
|
||||||
},
|
},
|
||||||
{ expiresIn: '365d' },
|
{ expiresIn: '365d' },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getNewAccessToken(refreshToken: string) {
|
async getNewAccessToken(refreshToken: string) {
|
||||||
const payload = this.jwtService.verify(refreshToken);
|
const payload: IRefreshPayload = this.jwtService.verify(refreshToken);
|
||||||
if (payload.type !== 'refresh') {
|
if (payload.type !== 'refresh') {
|
||||||
this.logger.error(`Token ${refreshToken} is not a refresh token`);
|
this.logger.error(`Token ${refreshToken} is not a refresh token`);
|
||||||
throw new HttpException('Invalid token', 401);
|
throw new HttpException('Invalid token', 401);
|
||||||
}
|
}
|
||||||
const user = await this.userRepo.findById(payload.id);
|
const user = await this.userRepo.findById(payload.id);
|
||||||
if (!user) {
|
if (
|
||||||
|
!user ||
|
||||||
|
payload['token_revision'] == undefined ||
|
||||||
|
payload['token_revision'] != user.pwRevision
|
||||||
|
) {
|
||||||
this.logger.error(`User ${payload.id} not found for refresh token`);
|
this.logger.error(`User ${payload.id} not found for refresh token`);
|
||||||
throw new HttpException('Invalid token', 401);
|
throw new HttpException('Invalid token', 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = this.createAccessToken(user);
|
const token = this.createAccessToken(user);
|
||||||
const pay = this.jwtService.decode(token);
|
const pay: IAccessPayload = this.jwtService.decode(token);
|
||||||
const result = {
|
const result = {
|
||||||
access_token: token,
|
access_token: token,
|
||||||
expires_in: pay.exp - pay.iat,
|
expires_in: pay.exp - pay.iat,
|
||||||
@@ -237,6 +248,7 @@ export class UsersService {
|
|||||||
try {
|
try {
|
||||||
const decoded = this.jwtService.verify(token);
|
const decoded = this.jwtService.verify(token);
|
||||||
this.activityRepo.logAccessTokenVerification();
|
this.activityRepo.logAccessTokenVerification();
|
||||||
|
console.log(decoded, '-');
|
||||||
return decoded;
|
return decoded;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.error(`Token ${token} is invalid. Error: ${e.message}`);
|
this.logger.error(`Token ${token} is invalid. Error: ${e.message}`);
|
||||||
@@ -303,6 +315,7 @@ export class UsersService {
|
|||||||
if (savedCode && savedCode.user) {
|
if (savedCode && savedCode.user) {
|
||||||
const hashedPassword = await bcrypt.hash(dto.password, 10);
|
const hashedPassword = await bcrypt.hash(dto.password, 10);
|
||||||
savedCode.user.password = hashedPassword;
|
savedCode.user.password = hashedPassword;
|
||||||
|
savedCode.user.pwRevision += 1;
|
||||||
await this.userRepo.save(savedCode.user);
|
await this.userRepo.save(savedCode.user);
|
||||||
await this.resetPwRepo.remove(savedCode);
|
await this.resetPwRepo.remove(savedCode);
|
||||||
await this.sessionRepo.delete({ user: { id: savedCode.user.id } });
|
await this.sessionRepo.delete({ user: { id: savedCode.user.id } });
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { ResetPwComponent } from './auth/reset-pw/reset-pw.component';
|
|||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{ path: 'login', component: LoginComponent, canActivate: [SessionKeyGuard] },
|
{ path: 'login', component: LoginComponent, canActivate: [SessionKeyGuard] },
|
||||||
|
{ path: 'authorize', component: LoginComponent, canActivate: [SessionKeyGuard] },
|
||||||
{ path: 'register', component: RegisterComponent },
|
{ path: 'register', component: RegisterComponent },
|
||||||
{ path: 'pw-reset', component: ResetPwComponent },
|
{ path: 'pw-reset', component: ResetPwComponent },
|
||||||
{ path: 'dashboard', component: DashboardComponent, canActivate: [SessionKeyGuard] },
|
{ path: 'dashboard', component: DashboardComponent, canActivate: [SessionKeyGuard] },
|
||||||
|
|||||||
@@ -125,6 +125,9 @@ body {
|
|||||||
outline: none;
|
outline: none;
|
||||||
border-bottom-color: #6A679E;
|
border-bottom-color: #6A679E;
|
||||||
}
|
}
|
||||||
|
.login__input_error {
|
||||||
|
border-bottom: 2px solid #b61d09;
|
||||||
|
}
|
||||||
|
|
||||||
.login__submit {
|
.login__submit {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ export class LoginComponent {
|
|||||||
this.client_id = params.client_id;
|
this.client_id = params.client_id;
|
||||||
|
|
||||||
if (!this.client_id) { return; }
|
if (!this.client_id) { return; }
|
||||||
|
console.log(params);
|
||||||
this.http.get<any>('api/auth/', {
|
this.http.get<any>('api/client', {
|
||||||
params
|
params
|
||||||
}).subscribe({
|
}).subscribe({
|
||||||
next: (client) => {
|
next: (client) => {
|
||||||
@@ -82,7 +82,7 @@ export class LoginComponent {
|
|||||||
|
|
||||||
login() {
|
login() {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
const url = this.client_id ? `api/auth/login?client_id=${this.client_id}` : 'api/app/login';
|
const url = this.client_id ? `api/login?client_id=${this.client_id}` : 'api/app/authorize';
|
||||||
console.log(url, this.client_id)
|
console.log(url, this.client_id)
|
||||||
this.http.post(url, this.loginForm.value).
|
this.http.post(url, this.loginForm.value).
|
||||||
pipe(
|
pipe(
|
||||||
|
|||||||
@@ -13,15 +13,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="login__field">
|
<div class="login__field">
|
||||||
<i class="login__icon fas fa-user user"></i>
|
<i class="login__icon fas fa-user user"></i>
|
||||||
<input formControlName="username" type="text" class="login__input" autocomplete="email" placeholder="User name / Email">
|
<input formControlName="username" type="text" class="login__input" autocomplete="email" placeholder="User name / Email" [class.login__input_error]="registerForm.controls.username.touched && registerForm.controls.username.invalid">
|
||||||
</div>
|
</div>
|
||||||
<div class="login__field">
|
<div class="login__field">
|
||||||
<i class="login__icon fas fa-lock safe"></i>
|
<i class="login__icon fas fa-lock safe"></i>
|
||||||
<input type="password" formControlName="password" autocomplete="new-password" class="login__input" placeholder="Password">
|
<input type="password" formControlName="password" autocomplete="new-password" class="login__input" placeholder="Password" [class.login__input_error]="registerForm.controls.password.touched && registerForm.controls.password.invalid">
|
||||||
</div>
|
</div>
|
||||||
<div class="login__field">
|
<div class="login__field">
|
||||||
<i class="login__icon fas fa-lock safe"></i>
|
<i class="login__icon fas fa-lock safe"></i>
|
||||||
<input type="password" formControlName="repeatPassword" autocomplete="new-password" class="login__input" placeholder="Repeat password">
|
<input type="password" formControlName="repeatPassword" autocomplete="new-password" class="login__input" placeholder="Repeat password" [class.login__input_error]="registerForm.controls.repeatPassword.touched && registerForm.controls.repeatPassword.invalid">
|
||||||
</div>
|
</div>
|
||||||
<button class="button login__submit" (click)="register()" [disabled]="!client_id || registerForm.invalid">
|
<button class="button login__submit" (click)="register()" [disabled]="!client_id || registerForm.invalid">
|
||||||
<span class="button__text">Register</span>
|
<span class="button__text">Register</span>
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ export class RegisterComponent {
|
|||||||
|
|
||||||
registerForm = new FormGroup({
|
registerForm = new FormGroup({
|
||||||
username: new FormControl('', [Validators.required, Validators.email, Validators.maxLength(100)]),
|
username: new FormControl('', [Validators.required, Validators.email, Validators.maxLength(100)]),
|
||||||
password: new FormControl('', [Validators.required, Validators.minLength(6), Validators.maxLength(20)]),
|
password: new FormControl('', [Validators.required, Validators.minLength(6), Validators.maxLength(64)]),
|
||||||
repeatPassword: new FormControl('', [Validators.required, Validators.minLength(6), Validators.maxLength(20)]),
|
repeatPassword: new FormControl('', [Validators.required, Validators.minLength(6), Validators.maxLength(64)]),
|
||||||
firstName: new FormControl('', [Validators.required, Validators.maxLength(100)]),
|
firstName: new FormControl('', [Validators.required, Validators.maxLength(100)]),
|
||||||
lastName: new FormControl('', [Validators.required, Validators.maxLength(100)]),
|
lastName: new FormControl('', [Validators.required, Validators.maxLength(100)]),
|
||||||
})
|
})
|
||||||
@@ -39,7 +39,7 @@ export class RegisterComponent {
|
|||||||
const params = (this.route.snapshot.queryParamMap as any)["params"];
|
const params = (this.route.snapshot.queryParamMap as any)["params"];
|
||||||
this.redirectUri = params.redirect_uri;
|
this.redirectUri = params.redirect_uri;
|
||||||
this.client_id = params.client_id;
|
this.client_id = params.client_id;
|
||||||
this.http.get<any>('api/auth/', {
|
this.http.get<any>('api/client', {
|
||||||
params
|
params
|
||||||
}).subscribe({
|
}).subscribe({
|
||||||
next: (client) => {
|
next: (client) => {
|
||||||
@@ -57,7 +57,7 @@ export class RegisterComponent {
|
|||||||
this.toast.error('Passwords do not match');
|
this.toast.error('Passwords do not match');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.http.post('api/auth/register?'+ 'client_id=' + this.client_id, this.registerForm.value).pipe(
|
this.http.post('api/register?'+ 'client_id=' + this.client_id, this.registerForm.value).pipe(
|
||||||
this.toast.observe({
|
this.toast.observe({
|
||||||
loading: 'Registering...',
|
loading: 'Registering...',
|
||||||
success: 'Registration successfull, please log in',
|
success: 'Registration successfull, please log in',
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export class ResetPwComponent {
|
|||||||
|
|
||||||
|
|
||||||
resetPassword() {
|
resetPassword() {
|
||||||
this.http.post('api/auth/reset', this.resetPw.value)
|
this.http.post('api/reset', this.resetPw.value)
|
||||||
.pipe(
|
.pipe(
|
||||||
this.toast.observe({
|
this.toast.observe({
|
||||||
loading: 'Sende Mail...',
|
loading: 'Sende Mail...',
|
||||||
@@ -59,7 +59,7 @@ export class ResetPwComponent {
|
|||||||
this.toast.error('Die Passwörter stimmen nicht überein');
|
this.toast.error('Die Passwörter stimmen nicht überein');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.http.post('api/auth/reset', this.setNewPwForm.value)
|
this.http.post('api/reset', this.setNewPwForm.value)
|
||||||
.pipe(
|
.pipe(
|
||||||
this.toast.observe({
|
this.toast.observe({
|
||||||
loading: 'Setze neues Passwort',
|
loading: 'Setze neues Passwort',
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export class SessionKeyGuard {
|
|||||||
const id = window.localStorage.getItem("auth_session_key");
|
const id = window.localStorage.getItem("auth_session_key");
|
||||||
if (!id ||id.length < 2) { return resolve(true); }
|
if (!id ||id.length < 2) { return resolve(true); }
|
||||||
|
|
||||||
const url = this.client_id ? 'api/auth/login-with-session-id' : 'api/auth/login-with-session-id/userlogin'
|
const url = this.client_id ? 'api/login-with-session-id' : 'api/login-with-session-id/userlogin'
|
||||||
|
|
||||||
this.http.post(url, {
|
this.http.post(url, {
|
||||||
code: id,
|
code: id,
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
<div mat-dialog-title>Hilfe</div>
|
||||||
|
<mat-dialog-content>
|
||||||
|
<div class="mat-body">
|
||||||
|
<h3>Authentifizierung:</h3>
|
||||||
|
<div>Für den Login:</div>
|
||||||
|
<code>https://sso.beantastic.de/authorize</code>
|
||||||
|
|
||||||
|
<h5>Query:</h5>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>response_type</td>
|
||||||
|
<td>code</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>client_id</td>
|
||||||
|
<td><CLIENT ID></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>redirect_uri</td>
|
||||||
|
<td><REDIRECT URI></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>scope</td>
|
||||||
|
<td><SCOPE></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<div>
|
||||||
|
Danach wird der user mit einem auth code als parameter <code>?code=<b><AUTH_CODE></b></code> zurückgeleitet.
|
||||||
|
</div>
|
||||||
|
<h3>Code => Accesstoken:</h3>
|
||||||
|
<div>den Code tauscht der Client gegen den Accesstoken:</div>
|
||||||
|
<code>POST: https://sso.beantastic.de/api/authorize</code>
|
||||||
|
<h5>Body (Form):</h5>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>client_id</td>
|
||||||
|
<td><CLIENT ID></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>client_secret</td>
|
||||||
|
<td><Secret></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>code</td>
|
||||||
|
<td><Auth Code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>grant_type</td>
|
||||||
|
<td>authorization code</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>Accesstoken prüfen:</h3>
|
||||||
|
<div>Einen Accesstoken verifizieren:</div>
|
||||||
|
<code>POST: https://sso.beantastic.de/api/verify</code>
|
||||||
|
<h5>Body (Form):</h5>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>access_token</td>
|
||||||
|
<td><Access Token></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<h5>Return:</h5>
|
||||||
|
Decoded Token
|
||||||
|
|
||||||
|
<h3>neuen Accesstoken:</h3>
|
||||||
|
<div>um einen Refreshtoken in einen Accesstoken zu tauschen:</div>
|
||||||
|
<code>POST: https://sso.beantastic.de/api/authorize</code>
|
||||||
|
<h5>Body (Form):</h5>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>client_id</td>
|
||||||
|
<td><CLIENT_ID></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>code</td>
|
||||||
|
<td><Refresh Token></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>grant_type</td>
|
||||||
|
<td>refreshtoken</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</mat-dialog-content>
|
||||||
|
|
||||||
|
<mat-dialog-actions>
|
||||||
|
<button mat-button mat-dialog-close >Schließen</button>
|
||||||
|
</mat-dialog-actions>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
h1, h2, h3, h4, h5 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { HelpComponent } from './help.component';
|
||||||
|
|
||||||
|
describe('HelpComponent', () => {
|
||||||
|
let component: HelpComponent;
|
||||||
|
let fixture: ComponentFixture<HelpComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [HelpComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(HelpComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-help',
|
||||||
|
standalone: true,
|
||||||
|
imports: [MatDialogModule, MatButtonModule],
|
||||||
|
templateUrl: './help.component.html',
|
||||||
|
styleUrl: './help.component.scss'
|
||||||
|
})
|
||||||
|
export class HelpComponent {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="title">SSO Beantastic</div>
|
<div class="title">SSO Beantastic</div>
|
||||||
<div class="flex-row">
|
<div class="flex-row">
|
||||||
|
<div class="question" (click)="openHelp()"></div>
|
||||||
|
|
||||||
<div> {{ userName }}</div>
|
<div> {{ userName }}</div>
|
||||||
<div class="logout" (click)="logout()" ></div>
|
|
||||||
|
<div class="logout" (click)="logout()" ></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logout{
|
.logout, .question {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { CreateClientComponent } from './components/create-client/create-client.
|
|||||||
import { CreateHotToastRef, HotToastService } from '@ngxpert/hot-toast';
|
import { CreateHotToastRef, HotToastService } from '@ngxpert/hot-toast';
|
||||||
import {MatBottomSheet, MatBottomSheetModule, MatBottomSheetRef} from '@angular/material/bottom-sheet';
|
import {MatBottomSheet, MatBottomSheetModule, MatBottomSheetRef} from '@angular/material/bottom-sheet';
|
||||||
import { LoginChartComponent } from './components/charts/login/login.chart.component';
|
import { LoginChartComponent } from './components/charts/login/login.chart.component';
|
||||||
|
import { HelpComponent } from './components/help/help.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-dashboard',
|
selector: 'app-dashboard',
|
||||||
@@ -38,7 +39,6 @@ export class DashboardComponent implements OnInit {
|
|||||||
this.router.navigateByUrl("/login");
|
this.router.navigateByUrl("/login");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.load();
|
this.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,6 +123,10 @@ export class DashboardComponent implements OnInit {
|
|||||||
logout() {
|
logout() {
|
||||||
this.userService.logout();
|
this.userService.logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openHelp() {
|
||||||
|
this.dialog.open(HelpComponent)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
1
idp_client/src/assets/icons/question.svg
Normal file
1
idp_client/src/assets/icons/question.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg enable-background="new 0 0 512 512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><g id="_x32_9_question"><g><path d="m72.632 129.761h178.052c2.702 0 4.888-2.186 4.888-4.888s-2.186-4.888-4.888-4.888h-178.052c-2.702 0-4.888 2.186-4.888 4.888s2.186 4.888 4.888 4.888z"/><path d="m72.632 177.873h108.583c2.702 0 4.888-2.186 4.888-4.888s-2.186-4.888-4.888-4.888h-108.583c-2.702 0-4.888 2.186-4.888 4.888s2.186 4.888 4.888 4.888z"/><path d="m72.632 225.984h217.16c2.702 0 4.888-2.186 4.888-4.888s-2.186-4.888-4.888-4.888h-217.16c-2.702 0-4.888 2.186-4.888 4.888-.001 2.702 2.186 4.888 4.888 4.888z"/><path d="m72.632 274.091h217.16c2.702 0 4.888-2.186 4.888-4.888s-2.186-4.888-4.888-4.888h-217.16c-2.702 0-4.888 2.186-4.888 4.888s2.186 4.888 4.888 4.888z"/><path d="m210.034 312.425h-137.402c-2.702 0-4.888 2.186-4.888 4.888s2.186 4.888 4.888 4.888h137.403c2.702 0 4.888-2.186 4.888-4.888s-2.187-4.888-4.889-4.888z"/><path d="m210.034 360.532h-137.402c-2.702 0-4.888 2.186-4.888 4.888s2.186 4.888 4.888 4.888h137.403c2.702 0 4.888-2.186 4.888-4.888s-2.187-4.888-4.889-4.888z"/><path d="m379.756 269.501v-165.91c0-1.047-.532-2.599-1.461-3.509l-92.685-90.829c-.881-.863-2.384-1.389-3.461-1.389h-216.754c-20.165 0-36.568 16.403-36.568 36.568v379.788c0 20.165 16.403 36.568 36.568 36.568h208.864c21.614 26.436 54.462 43.347 91.195 43.347 64.911 0 117.72-52.828 117.72-117.768 0-60.092-45.232-109.779-103.418-116.866zm-92.68-245.078 75.799 74.26h-66.562c-5.094 0-9.238-4.168-9.238-9.29v-64.97zm-221.681 426.589c-14.77 0-26.791-12.021-26.791-26.791v-379.789c0-14.77 12.021-26.791 26.791-26.791h211.904v71.752c0 10.512 8.531 19.067 19.014 19.067h73.666v160.256c-1.504-.057-3.008-.115-4.526-.115-64.939 0-117.768 52.828-117.768 117.768 0 23.861 7.153 46.073 19.397 64.643zm300.059 43.347c-59.545 0-107.991-48.446-107.991-107.991s48.446-107.991 107.991-107.991c59.521 0 107.943 48.446 107.943 107.991s-48.422 107.991-107.943 107.991z"/><path d="m367.354 319.553h-3.848c-19.707 0-35.737 16.036-35.737 35.742 0 2.702 2.186 4.888 4.888 4.888s4.888-2.186 4.888-4.888c0-14.317 11.644-25.965 25.961-25.965h3.848c14.317 0 25.965 11.648 25.965 25.965 0 28.204-25.253 36.571-26.419 37.571-8.24 4.187-13.362 12.522-13.362 21.764v11.534c0 2.702 2.186 4.888 4.888 4.888s4.888-2.186 4.888-4.888v-11.534c0-5.538 3.07-10.536 8.011-13.047 1.123-.978 31.77-11.54 31.77-46.288.001-19.707-16.035-35.742-35.741-35.742z"/><path d="m358.36 440.514c-2.702 0-4.888 2.186-4.888 4.888v2.893c0 2.702 2.186 4.888 4.888 4.888s4.888-2.186 4.888-4.888v-2.893c0-2.702-2.186-4.888-4.888-4.888z"/></g></g><g id="Layer_1"/></svg>
|
||||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -20,6 +20,10 @@ html, body {
|
|||||||
background-image: url("assets/icons/logout.svg");
|
background-image: url("assets/icons/logout.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.question {
|
||||||
|
background-image: url("assets/icons/question.svg");
|
||||||
|
}
|
||||||
|
|
||||||
.flex-row{
|
.flex-row{
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|||||||
Reference in New Issue
Block a user