fix(auth): accept only access tokens as bearer credentials (MFA bypass)
All JWTs (access, MFA setup, MFA challenge, SSO completion) were signed with the same secret and the access-token strategy checked only signature and expiry. The MFA challenge token returned after the password step therefore worked as a full access token: a password alone was enough to use the API, skipping the second factor. Every token now carries a pinned issuer, algorithm (HS256) and a per-purpose audience; the strategy accepts only the access audience and rejects any payload carrying a helper-token purpose. Existing access tokens (no audience) stop working; clients renew them via the refresh cookie. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
fac66882fa
commit
e0bd7b2095
6 files changed
+176
-20
No files matched your search
@@ -8,6 +8,7 @@ import { AuditLogService } from '../audit/audit-log.service';
|
||||
import { PermissionResolverService } from './permissions/permission-resolver.service';
|
||||
import { MfaService } from './mfa/mfa.service';
|
||||
import { parseDurationToMs } from './util/duration';
|
||||
import { TOKEN_AUDIENCE, jwtSignOptions } from './util/jwt-claims';
|
||||
|
||||
interface AuthTokens {
|
||||
accessToken: string;
|
||||
@@ -72,10 +73,7 @@ export class AuthService {
|
||||
);
|
||||
return this.jwtService.sign(
|
||||
{ sub: user.id, email: user.email },
|
||||
{
|
||||
secret: process.env.JWT_ACCESS_SECRET,
|
||||
expiresIn: expiresInSeconds,
|
||||
},
|
||||
jwtSignOptions(TOKEN_AUDIENCE.access, expiresInSeconds),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user