-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(step-up): add stepup validator (#176)
- Loading branch information
1 parent
64466c9
commit 94559ae
Showing
14 changed files
with
269 additions
and
3 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
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
7 changes: 7 additions & 0 deletions
7
src/clients/identity/exceptions/max-age-exceeded.exception.ts
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,7 @@ | ||
import { StatusCodeError } from './status-code-error.exception'; | ||
|
||
export class MaxAgeExceededException extends StatusCodeError { | ||
constructor() { | ||
super(401, 'Max age exceeded'); | ||
} | ||
} |
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,7 @@ | ||
import { StatusCodeError } from './status-code-error.exception'; | ||
|
||
export class MissingAcrException extends StatusCodeError { | ||
constructor(acr: string) { | ||
super(401, `Missing ACR: ${acr}`); | ||
} | ||
} |
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,7 @@ | ||
import { StatusCodeError } from './status-code-error.exception'; | ||
|
||
export class MissingAmrException extends StatusCodeError { | ||
constructor() { | ||
super(401, `AMR is missing`); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from './identity-client'; | ||
export * from './step-up'; |
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,3 @@ | ||
export const STEP_UP_ACR_VALUE = 'http://schemas.openid.net/pape/policies/2007/06/multi-factor'; | ||
export const AMR_MFA_VALUE = 'mfa'; | ||
export const AMR_METHOD_VALUE = ['otp', 'sms', 'hwk']; |
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,3 @@ | ||
export * from './constants'; | ||
export * from './types'; | ||
export * from './step-up.validator'; |
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,70 @@ | ||
import { MaxAgeExceededException, MissingAcrException, MissingAmrException } from '../exceptions'; | ||
import { AMR_METHOD_VALUE, AMR_MFA_VALUE, STEP_UP_ACR_VALUE } from './constants'; | ||
import { StepupValidator } from './step-up.validator'; | ||
|
||
describe('StepUpValidator', () => { | ||
describe('validateStepUp', () => { | ||
it('should throw MissingAcrException if acr is not STEP_UP_ACR_VALUE', () => { | ||
const dto = { acr: 'fake-acr' }; | ||
|
||
const act = () => StepupValidator.validateStepUp(dto, {}); | ||
|
||
expect(act).toThrow(MissingAcrException); | ||
}); | ||
|
||
it('should throw MissingAmrException if amr does not include AMR_MFA_VALUE', () => { | ||
const dto = { acr: STEP_UP_ACR_VALUE, amr: ['fake-amr'] }; | ||
|
||
const act = () => StepupValidator.validateStepUp(dto, {}); | ||
|
||
expect(act).toThrow(MissingAmrException); | ||
}); | ||
|
||
it('should throw MissingAmrException if amr does not include AMR_METHOD_VALUE', () => { | ||
const dto = { acr: STEP_UP_ACR_VALUE, amr: [AMR_MFA_VALUE] }; | ||
const stepUpOptions = {}; | ||
|
||
const act = () => StepupValidator.validateStepUp(dto, stepUpOptions); | ||
|
||
expect(act).toThrow(MissingAmrException); | ||
}); | ||
|
||
it('should throw MaxAgeExceededException if maxAge is exceeded', () => { | ||
const dto = { | ||
acr: STEP_UP_ACR_VALUE, | ||
amr: [AMR_MFA_VALUE, AMR_METHOD_VALUE[0]], | ||
auth_time: Date.now() / 1000 - 20, | ||
}; | ||
const stepUpOptions = { maxAge: 5 }; | ||
|
||
const act = () => StepupValidator.validateStepUp(dto, stepUpOptions); | ||
|
||
expect(act).toThrow(MaxAgeExceededException); | ||
}); | ||
|
||
it('should throw MaxAgeExceededException if maxAge is specified but auth_time is missing', () => { | ||
const dto = { | ||
acr: STEP_UP_ACR_VALUE, | ||
amr: [AMR_MFA_VALUE, AMR_METHOD_VALUE[0]], | ||
}; | ||
const stepUpOptions = { maxAge: 5 }; | ||
|
||
const act = () => StepupValidator.validateStepUp(dto, stepUpOptions); | ||
|
||
expect(act).toThrow(MaxAgeExceededException); | ||
}); | ||
|
||
it('should not throw if acr is STEP_UP_ACR_VALUE, amr includes AMR_MFA_VALUE and AMR_METHOD_VALUE and maxAge is not exceeded', () => { | ||
const dto = { | ||
acr: STEP_UP_ACR_VALUE, | ||
amr: [AMR_MFA_VALUE, AMR_METHOD_VALUE[0]], | ||
auth_time: Date.now() / 1000 - 20, | ||
}; | ||
const stepUpOptions = { maxAge: 1000 }; | ||
|
||
const act = () => StepupValidator.validateStepUp(dto, stepUpOptions); | ||
|
||
expect(act).not.toThrow(); | ||
}); | ||
}); | ||
}); |
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,34 @@ | ||
import { STEP_UP_ACR_VALUE, AMR_METHOD_VALUE, AMR_MFA_VALUE } from './constants'; | ||
import { IValidateStepupTokenOptions, ValidateStepupFields } from './types'; | ||
import { MaxAgeExceededException, MissingAcrException, MissingAmrException } from '../exceptions'; | ||
import Logger from '../../../components/logger'; | ||
|
||
export class StepupValidator { | ||
public static validateStepUp(dto: ValidateStepupFields, stepUpOptions: IValidateStepupTokenOptions = {}): void { | ||
const { acr, amr } = dto; | ||
const { maxAge } = stepUpOptions; | ||
|
||
const isACRValid = acr === STEP_UP_ACR_VALUE; | ||
|
||
if (!isACRValid) { | ||
Logger.info('Invalid ACR', { acr: acr }); | ||
throw new MissingAcrException(STEP_UP_ACR_VALUE); | ||
} | ||
|
||
const isAMRIncludesMFA = amr?.includes(AMR_MFA_VALUE); | ||
const isAMRIncludesMethod = AMR_METHOD_VALUE.find((method) => amr?.includes(method)); | ||
|
||
if (!(isAMRIncludesMFA && isAMRIncludesMethod)) { | ||
Logger.info('Invalid AMR', { amr: amr }); | ||
throw new MissingAmrException(); | ||
} | ||
|
||
if (maxAge) { | ||
const diff = Date.now() / 1000 - (dto.auth_time ?? 0); | ||
if (diff > maxAge) { | ||
Logger.info('Max age exceeded', { maxAge: maxAge, authTime: dto.auth_time }); | ||
throw new MaxAgeExceededException(); | ||
} | ||
} | ||
} | ||
} |
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,9 @@ | ||
export interface ValidateStepupFields { | ||
amr?: string[]; | ||
acr?: string; | ||
auth_time?: number; | ||
} | ||
|
||
export interface IValidateStepupTokenOptions { | ||
maxAge?: number; | ||
} |
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