mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
feat(web,server): manage authorized devices (#2329)
* feat: manage authorized devices * chore: open api * get header from mobile app * write header from mobile app * styling * fix unit test * feat: use relative time * feat: update access time * fix: tests * chore: confirm wording * chore: bump test coverage thresholds * feat: add some icons * chore: icon tweaks --------- Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
parent
aa91b946fa
commit
b8313abfa8
41 changed files with 1209 additions and 93 deletions
|
|
@ -12,7 +12,7 @@ import { OAuthCore } from '../oauth/oauth.core';
|
|||
import { INITIAL_SYSTEM_CONFIG, ISystemConfigRepository } from '../system-config';
|
||||
import { IUserRepository, UserCore } from '../user';
|
||||
import { AuthType, IMMICH_ACCESS_COOKIE } from './auth.constant';
|
||||
import { AuthCore } from './auth.core';
|
||||
import { AuthCore, LoginDetails } from './auth.core';
|
||||
import { ICryptoRepository } from '../crypto/crypto.repository';
|
||||
import { AuthUserDto, ChangePasswordDto, LoginCredentialDto, SignUpDto } from './dto';
|
||||
import { AdminSignupResponseDto, LoginResponseDto, LogoutResponseDto, mapAdminSignupResponse } from './response-dto';
|
||||
|
|
@ -21,6 +21,7 @@ import cookieParser from 'cookie';
|
|||
import { ISharedLinkRepository, ShareCore } from '../share';
|
||||
import { APIKeyCore } from '../api-key/api-key.core';
|
||||
import { IKeyRepository } from '../api-key';
|
||||
import { AuthDeviceResponseDto, mapUserToken } from './response-dto';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
|
|
@ -53,8 +54,7 @@ export class AuthService {
|
|||
|
||||
public async login(
|
||||
loginCredential: LoginCredentialDto,
|
||||
clientIp: string,
|
||||
isSecure: boolean,
|
||||
loginDetails: LoginDetails,
|
||||
): Promise<{ response: LoginResponseDto; cookie: string[] }> {
|
||||
if (!this.authCore.isPasswordLoginEnabled()) {
|
||||
throw new UnauthorizedException('Password login has been disabled');
|
||||
|
|
@ -69,16 +69,18 @@ export class AuthService {
|
|||
}
|
||||
|
||||
if (!user) {
|
||||
this.logger.warn(`Failed login attempt for user ${loginCredential.email} from ip address ${clientIp}`);
|
||||
this.logger.warn(
|
||||
`Failed login attempt for user ${loginCredential.email} from ip address ${loginDetails.clientIp}`,
|
||||
);
|
||||
throw new BadRequestException('Incorrect email or password');
|
||||
}
|
||||
|
||||
return this.authCore.createLoginResponse(user, AuthType.PASSWORD, isSecure);
|
||||
return this.authCore.createLoginResponse(user, AuthType.PASSWORD, loginDetails);
|
||||
}
|
||||
|
||||
public async logout(authUser: AuthUserDto, authType: AuthType): Promise<LogoutResponseDto> {
|
||||
if (authUser.accessTokenId) {
|
||||
await this.userTokenCore.deleteToken(authUser.accessTokenId);
|
||||
await this.userTokenCore.delete(authUser.id, authUser.accessTokenId);
|
||||
}
|
||||
|
||||
if (authType === AuthType.OAUTH) {
|
||||
|
|
@ -152,6 +154,15 @@ export class AuthService {
|
|||
throw new UnauthorizedException('Authentication required');
|
||||
}
|
||||
|
||||
async getDevices(authUser: AuthUserDto): Promise<AuthDeviceResponseDto[]> {
|
||||
const userTokens = await this.userTokenCore.getAll(authUser.id);
|
||||
return userTokens.map((userToken) => mapUserToken(userToken, authUser.accessTokenId));
|
||||
}
|
||||
|
||||
async logoutDevice(authUser: AuthUserDto, deviceId: string): Promise<void> {
|
||||
await this.userTokenCore.delete(authUser.id, deviceId);
|
||||
}
|
||||
|
||||
private getBearerToken(headers: IncomingHttpHeaders): string | null {
|
||||
const [type, token] = (headers.authorization || '').split(' ');
|
||||
if (type.toLowerCase() === 'bearer') {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue