fix(ihm_client): génération et persistance locale d'une clé HMAC si VITE_JWT_SECRET_KEY absent
All checks were successful
Build and Push Docker image (ext) / docker (push) Successful in 50s
All checks were successful
Build and Push Docker image (ext) / docker (push) Successful in 50s
This commit is contained in:
parent
a6f54dc8f5
commit
4f7d51e9b2
@ -21,9 +21,40 @@ export default class TokenService {
|
||||
return TokenService.instance;
|
||||
}
|
||||
|
||||
private getOrCreateSecret(): Uint8Array {
|
||||
// Priorité à la variable d'environnement si définie et non vide
|
||||
if (this.SECRET_KEY && String(this.SECRET_KEY).trim().length > 0) {
|
||||
return new Uint8Array(this.encoder.encode(this.SECRET_KEY));
|
||||
}
|
||||
|
||||
// Sinon, on persiste une clé aléatoire locale (stable entre sessions) côté navigateur
|
||||
try {
|
||||
const storageKey = 'ihm_jwt_secret_key_v1';
|
||||
let secretB64 = localStorage.getItem(storageKey);
|
||||
if (!secretB64) {
|
||||
const random = new Uint8Array(32);
|
||||
crypto.getRandomValues(random);
|
||||
// Stocker en base64 pour être textuel
|
||||
secretB64 = btoa(String.fromCharCode(...random));
|
||||
localStorage.setItem(storageKey, secretB64);
|
||||
}
|
||||
// Décoder base64 → Uint8Array
|
||||
const binary = atob(secretB64);
|
||||
const bytes = new Uint8Array(binary.length);
|
||||
for (let i = 0; i < binary.length; i++) {
|
||||
bytes[i] = binary.charCodeAt(i);
|
||||
}
|
||||
return bytes;
|
||||
} catch (_e) {
|
||||
// Fallback minimal si localStorage indisponible (mode très restrictif)
|
||||
const fallback = 'fallback-secret-key-please-persist';
|
||||
return new Uint8Array(this.encoder.encode(fallback));
|
||||
}
|
||||
}
|
||||
|
||||
async generateSessionToken(origin: string): Promise<TokenPair> {
|
||||
const secret = new Uint8Array(this.encoder.encode(this.SECRET_KEY));
|
||||
|
||||
const secret = this.getOrCreateSecret();
|
||||
|
||||
const accessToken = await new jose.SignJWT({ origin, type: 'access' })
|
||||
.setProtectedHeader({ alg: 'HS256' })
|
||||
.setIssuedAt()
|
||||
@ -41,16 +72,16 @@ export default class TokenService {
|
||||
|
||||
async validateToken(token: string, origin: string): Promise<boolean> {
|
||||
try {
|
||||
const secret = new Uint8Array(this.encoder.encode(this.SECRET_KEY));
|
||||
const secret = this.getOrCreateSecret();
|
||||
const { payload } = await jose.jwtVerify(token, secret);
|
||||
|
||||
|
||||
return payload.origin === origin;
|
||||
} catch (error: any) {
|
||||
if (error?.code === 'ERR_JWT_EXPIRED') {
|
||||
console.log('Token expiré');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
console.error('Erreur de validation du token:', error);
|
||||
return false;
|
||||
}
|
||||
@ -65,7 +96,7 @@ export default class TokenService {
|
||||
}
|
||||
|
||||
// Vérifier le type du token
|
||||
const secret = new Uint8Array(this.encoder.encode(this.SECRET_KEY));
|
||||
const secret = this.getOrCreateSecret();
|
||||
const { payload } = await jose.jwtVerify(refreshToken, secret);
|
||||
if (payload.type !== 'refresh') {
|
||||
return null;
|
||||
|
Loading…
x
Reference in New Issue
Block a user