-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: 인증 단계에서 라라벨 세션을 공유하도록 구현 (#64)
- Loading branch information
Showing
9 changed files
with
142 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { ConfigService } from '@nestjs/config'; | ||
import fs from 'fs/promises'; | ||
import crypto from 'crypto'; | ||
import { unserialize } from 'php-serialize'; | ||
|
||
import { LaravelSessionConfig } from '../config/LaravelSessionConfig'; | ||
|
||
type EncryptedSession = { | ||
iv: string; | ||
value: string; | ||
mac: string; | ||
}; | ||
|
||
type SessionData = { | ||
_token: string; | ||
_previous: { | ||
url: string; | ||
}; | ||
_flash: { | ||
old: any; | ||
new: any; | ||
}; | ||
[key: `login_${string}`]: number | undefined; | ||
}; | ||
|
||
@Injectable() | ||
export class LaravelAuthnAdapter { | ||
private readonly config: LaravelSessionConfig; | ||
|
||
constructor(configService: ConfigService) { | ||
this.config = configService.getOrThrow<LaravelSessionConfig>('session'); | ||
} | ||
|
||
async authenticate(rawSession: string): Promise<string | null> { | ||
const session = this.normalize(rawSession); | ||
const decrypted = this.decrypt(session); | ||
|
||
const sessionId = unserialize(decrypted); | ||
const serializedSessionData = await this.getSessionData(sessionId); | ||
|
||
const sessionData: SessionData = unserialize(serializedSessionData); | ||
const userId = this.getUserId(sessionData); | ||
|
||
return userId; | ||
} | ||
|
||
private normalize(session: string): EncryptedSession { | ||
const urlDecoded = decodeURIComponent(session); | ||
const base64Decoded = Buffer.from(urlDecoded, 'base64').toString('utf-8'); | ||
return JSON.parse(base64Decoded); | ||
} | ||
|
||
private decrypt(session: EncryptedSession): string { | ||
const key = this.config.secret; | ||
|
||
const iv = Buffer.from(session.iv, 'base64'); | ||
const value = Buffer.from(session.value, 'base64'); | ||
|
||
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); | ||
const decrypted = Buffer.concat([decipher.update(value), decipher.final()]); | ||
|
||
const hash = crypto | ||
.createHmac('sha256', key) | ||
.update(session.iv + session.value) | ||
.digest() | ||
.toString('hex'); | ||
|
||
if (session.mac !== hash) { | ||
throw new Error('Invalid MAC'); | ||
} | ||
|
||
return decrypted.toString(); | ||
} | ||
|
||
private async getSessionData(sessionId: string): Promise<string> { | ||
const path = `${this.config.storagePath}/${sessionId}`; | ||
return await fs.readFile(path, { encoding: 'utf8' }); | ||
} | ||
|
||
private getUserId(sessionData: SessionData): string | null { | ||
const loginKey = Object.keys(sessionData).find((key) => | ||
key.startsWith('login_'), | ||
); | ||
return loginKey ? String(sessionData[loginKey]) : null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
export const UserRole = { | ||
USER: 'USER', | ||
ADMIN: 'ADMIN', | ||
MANAGER: 'MANAGER', | ||
} as const; | ||
export type UserRole = (typeof UserRole)[keyof typeof UserRole]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
export type LaravelSessionConfig = { | ||
secret: string; | ||
storagePath: string; | ||
}; | ||
|
||
// 레거시 시스템과의 호환성을 위해 Laravel 세션을 사용합니다. | ||
export const config = (): LaravelSessionConfig => ({ | ||
secret: process.env.APP_KEY || '', | ||
storagePath: process.env.SESSION_STORAGE_PATH || '', | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,10 @@ | ||
import * as db from '@sight/core/config/DatabaseConfig'; | ||
import * as session from '@sight/core/config/LaravelSessionConfig'; | ||
|
||
export const configuration = (): { | ||
database: db.DatabaseConfig; | ||
session: session.LaravelSessionConfig; | ||
} => ({ | ||
database: db.config(), | ||
session: session.config(), | ||
}); |