88 lines
2.7 KiB
TypeScript
88 lines
2.7 KiB
TypeScript
import { Controller, Get, Param, Post, Query, Res, UseInterceptors } from '@nestjs/common';
|
|
import { randomUUID } from 'node:crypto';
|
|
|
|
import { MVCResponse, Views } from '../mvc.types';
|
|
import { StateManagerService } from './state-manager.service';
|
|
import { Authentication } from '../../../domain/authentication.types';
|
|
import { Identity } from '../../../domain/identity.types';
|
|
import { LoginContextInterceptor } from './login-context.interceptor';
|
|
import { IdentityAuthDeviceDao } from '../../../persistence/identity-auth-device.dao';
|
|
import { Context } from './context.decorator';
|
|
|
|
@Controller({
|
|
version: '1',
|
|
path: 'auth/:realm/signin/identifier',
|
|
})
|
|
export class IdentifierController {
|
|
|
|
constructor(
|
|
private readonly stateManager: StateManagerService,
|
|
private readonly identityAuthDeviceDao: IdentityAuthDeviceDao,
|
|
) {}
|
|
|
|
@Get()
|
|
async getLogin(
|
|
@Param('realm') realm: string,
|
|
@Res() res: MVCResponse,
|
|
) {
|
|
|
|
const state = await this.stateManager.getNewState();
|
|
|
|
return res.render(Views.LoginView, {
|
|
realm,
|
|
state,
|
|
user_settings: {
|
|
theme: 'dark',
|
|
},
|
|
links: {
|
|
identifier_form: `/auth/${realm}/signin/identifier`
|
|
}
|
|
});
|
|
}
|
|
|
|
@Post()
|
|
@UseInterceptors(LoginContextInterceptor)
|
|
async postLogin(
|
|
@Param('realm') realm: string,
|
|
@Context() context: Authentication.Login.RequestContext,
|
|
@Res() res: MVCResponse,
|
|
) {
|
|
|
|
const devicesForUser = await this.identityAuthDeviceDao.findByRealmAndUsername(realm, context.username);
|
|
|
|
if (devicesForUser.length === 0) {
|
|
return res.render(Views.LoginPasswordChallenge, {
|
|
realm,
|
|
state: this.stateManager.updateState(context.state),
|
|
username: context.username,
|
|
user_settings: {
|
|
theme: 'dark',
|
|
},
|
|
links: {
|
|
select_device: null,
|
|
challenge_form: `/auth/${realm}/signin/challenge/${randomUUID()}`,
|
|
try_different_user: `/auth/${realm}/signin/identifier`,
|
|
}
|
|
});
|
|
}
|
|
|
|
const selectedDevice = devicesForUser.length === 1 ? devicesForUser[0] : devicesForUser.find(d => d.preferred);
|
|
|
|
if (selectedDevice?.deviceType === Identity.AuthDevice.Type.Password) {
|
|
return res.render(Views.LoginPasswordChallenge, {
|
|
realm,
|
|
state: this.stateManager.updateState(context.state),
|
|
username: context.username,
|
|
user_settings: {
|
|
theme: 'dark',
|
|
},
|
|
links: {
|
|
select_device: devicesForUser.length > 1 ? `/auth/${realm}/signin/challenge` : null,
|
|
challenge_form: `/auth/${realm}/signin/challenge/${selectedDevice.urn}`,
|
|
try_different_user: `/auth/${realm}/signin/identifier`,
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|