Skip to content

Commit

Permalink
Account Service SIWF v2 URI Validation (#669)
Browse files Browse the repository at this point in the history
# Problem

Updates to support siwf v2.1.0

Closes #640 

# Solution

- Renamed `SIWF_V2_DOMAIN` to `SIWF_V2_URI_VALIDATION` and follow on
config changes, tests, etc.
- Renamed `loginMsgDomain` to `loginMsgURIValidation`
- Updated `ENVIRONMENT.md`
  • Loading branch information
mattheworris authored Nov 1, 2024
1 parent 7f3fd81 commit f1818cf
Show file tree
Hide file tree
Showing 12 changed files with 1,759 additions and 2,152 deletions.
21 changes: 14 additions & 7 deletions apps/account-api/src/api.config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('Account API Config', () => {
GRAPH_ENVIRONMENT_TYPE: undefined,
SIWF_URL: undefined,
SIWF_V2_URL: undefined,
SIWF_V2_DOMAIN: undefined,
SIWF_V2_URI_VALIDATION: undefined,
};

beforeAll(() => {
Expand All @@ -41,12 +41,6 @@ describe('Account API Config', () => {
it('invalid api timeout limit should fail', async () => shouldFailBadValues(ALL_ENV, 'API_TIMEOUT_MS', [0]));

it('invalid url for SIWF_V2_URL', async () => shouldFailBadValues(ALL_ENV, 'SIWF_V2_URL', ['sdfdsf']));

it('invalid url SIWF_V2_DOMAIN', async () =>
shouldFailBadValues(ALL_ENV, 'SIWF_V2_DOMAIN', ['https://www.example.com']));

it('invalid with port SIWF_V2_DOMAIN', async () =>
shouldFailBadValues(ALL_ENV, 'SIWF_V2_DOMAIN', ['localhost:3000']));
});

describe('valid environment', () => {
Expand Down Expand Up @@ -75,6 +69,19 @@ describe('Account API Config', () => {
expect(accountServiceConfig.siwfUrl).toStrictEqual(ALL_ENV.SIWF_URL);
});

it('should get SIWF V2 URL', () => {
expect(accountServiceConfig.siwfV2Url).toStrictEqual(ALL_ENV.SIWF_V2_URL);
});

it.each([['https://example.com/login'], ['example://login'], ['localhost'], ['localhost:3030/login/path']])(
'should get SIWF V2 URI VALIDATION for %s',
async (value) => {
ALL_ENV.SIWF_V2_URI_VALIDATION = value;
accountServiceConfig = await setupConfigService(ALL_ENV);
expect(accountServiceConfig.siwfV2URIValidation).toStrictEqual(value);
},
);

it('should get api timeout limit milliseconds', () => {
expect(accountServiceConfig.apiTimeoutMs).toStrictEqual(parseInt(ALL_ENV.API_TIMEOUT_MS as string, 10));
});
Expand Down
8 changes: 4 additions & 4 deletions apps/account-api/src/api.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface IAccountApiConfig {
graphEnvironmentType: keyof EnvironmentType;
siwfUrl: string;
siwfV2Url?: string;
siwfV2Domain?: string;
siwfV2URIValidation?: string;
}

export default registerAs('account-api', (): IAccountApiConfig => {
Expand Down Expand Up @@ -44,10 +44,10 @@ export default registerAs('account-api', (): IAccountApiConfig => {
value: process.env.SIWF_V2_URL,
joi: Joi.string().optional().allow(null).allow('').empty('').uri(),
},
siwfV2Domain: {
value: process.env.SIWF_V2_DOMAIN,
siwfV2URIValidation: {
value: process.env.SIWF_V2_URI_VALIDATION,
// Allow localhost specifically
joi: Joi.string().optional().allow(null).allow('localhost').allow('').empty('').domain(),
joi: Joi.string().optional().allow(null).allow('localhost').allow('').empty('').uri(),
},
};

Expand Down
4 changes: 2 additions & 2 deletions apps/account-api/src/controllers/v2/accounts-v2.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ export class AccountsControllerV2 {
async postSignInWithFrequency(@Body() callbackRequest: WalletV2LoginRequestDto): Promise<WalletV2LoginResponseDto> {
this.logger.debug('Received Sign In With Frequency v2 callback', JSON.stringify(callbackRequest));

if (!this.accountConfig.siwfV2Domain) {
this.logger.error('"SIWF_V2_DOMAIN" required to use SIWF v2');
if (!this.accountConfig.siwfV2URIValidation) {
this.logger.error('"SIWF_V2_URI_VALIDATION" required to use SIWF v2');
throw new ForbiddenException('SIWF v2 processing unavailable');
}

Expand Down
10 changes: 7 additions & 3 deletions apps/account-api/src/services/siwfV2.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const mockAccountApiConfigProvider = GenerateMockConfigProvider<IAccountApiConfi
graphEnvironmentType: 0,
siwfUrl: '',
siwfV2Url: 'https://www.example.com/siwf',
siwfV2Domain: 'localhost',
siwfV2URIValidation: 'localhost',
});

const exampleCallback = 'https://www.example.com/callback';
Expand Down Expand Up @@ -214,7 +214,9 @@ describe('SiwfV2Service', () => {
});

it('Should throw BadRequest if the payload is for a different domain', async () => {
jest.spyOn(mockAccountApiConfigProvider.useValue, 'siwfV2Domain', 'get').mockReturnValue('bad.example.com');
jest
.spyOn(mockAccountApiConfigProvider.useValue, 'siwfV2URIValidation', 'get')
.mockReturnValue('bad.example.com');
await expect(
siwfV2Service.getPayload({
authorizationPayload: base64url(JSON.stringify(validSiwfLoginResponsePayload)),
Expand All @@ -223,7 +225,9 @@ describe('SiwfV2Service', () => {
});

it('Should throw BadRequest if the response is for a different domain', async () => {
jest.spyOn(mockAccountApiConfigProvider.useValue, 'siwfV2Domain', 'get').mockReturnValue('bad.example.com');
jest
.spyOn(mockAccountApiConfigProvider.useValue, 'siwfV2URIValidation', 'get')
.mockReturnValue('bad.example.com');

const validAuthCode = 'valid-auth-code';
jest.spyOn(siwfV2Service as any, 'swifV2Endpoint').mockReturnValue('https://siwf.example.com');
Expand Down
6 changes: 3 additions & 3 deletions apps/account-api/src/services/siwfV2.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,11 @@ export class SiwfV2Service {

async getPayload(request: WalletV2LoginRequestDto): Promise<SiwfResponse> {
let payload: SiwfResponse;
const loginMsgDomain = this.apiConf.siwfV2Domain;
const loginMsgURIValidation = this.apiConf.siwfV2URIValidation;
if (request.authorizationPayload) {
try {
// Await here so the error is caught
payload = await validateSiwfResponse(request.authorizationPayload, loginMsgDomain);
payload = await validateSiwfResponse(request.authorizationPayload, loginMsgURIValidation);
} catch (e) {
this.logger.warn('Failed to parse "authorizationPayload"', { error: e.toString() });
throw new BadRequestException('Invalid `authorizationPayload` in request.');
Expand All @@ -138,7 +138,7 @@ export class SiwfV2Service {
try {
payload = await getLoginResult(request.authorizationCode, {
endpoint: this.swifV2Endpoint(),
loginMsgDomain,
loginMsgUri: loginMsgURIValidation,
});
} catch (e) {
this.logger.warn('Failed to retrieve valid payload from "authorizationCode"', { error: e.toString() });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ data:
HEALTH_CHECK_SUCCESS_THRESHOLD: {{ .Values.account.env.HEALTH_CHECK_SUCCESS_THRESHOLD | quote }}
CAPACITY_LIMIT: {{ .Values.account.env.CAPACITY_LIMIT | quote }}
SIWF_URL: {{ .Values.account.env.SIWF_URL | quote }}
SIWF_DOMAIN: {{ .Values.account.env.SIWF_DOMAIN | quote }}
SIWF_V2_URL: {{ .Values.account.env.SIWF_V2_URL | quote }}
SIWF_V2_URI_VALIDATION: {{ .Values.account.env.SIWF_V2_URI_VALIDATION | quote }}
DEBUG: {{ .Values.account.env.DEBUG | quote }}
CACHE_KEY_PREFIX: {{ .Values.account.env.CACHE_KEY_PREFIX | quote }}
GRAPH_ENVIRONMENT_TYPE: {{ .Values.account.env.GRAPH_ENVIRONMENT_TYPE | quote }}
Expand Down
2 changes: 1 addition & 1 deletion developer-docs/account/ENVIRONMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ This application recognizes the following environment variables:
| `API_BODY_JSON_LIMIT` | Api json body size limit in string (some examples: 100kb or 5mb or etc) | string | | 1mb |
| `SIWF_URL` | SIWF v1: URL for Sign In With Frequency V1 UI | URL | | https://ProjectLibertyLabs.github.io/siwf/v1/ui |
| `SIWF_V2_URL` | SIWF v2: URL for Sign In With Frequency V2 Redirect URL | URL | | Frequency Access |
| `SIWF_V2_DOMAIN` | SIWF v2: Domain to validate signin requests (\*Required if using Sign In with Frequency v2) | Domain (Example: www.frequency.xyz) | \* | |
| `SIWF_V2_URI_VALIDATION` | SIWF v2: Domain (formatted as URI) to validate signin requests (\*Required if using Sign In with Frequency v2) | Domain (Examples: https://www.your-app.com, example://login, localhost) | \* | |
1 change: 1 addition & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ x-account-service-env: &account-service-env
WEBHOOK_BASE_URL: 'http://mock-webhook-logger:${ACCOUNT_WEBHOOK_PORT:-3001}/webhooks/account-service'
GRAPH_ENVIRONMENT_TYPE: Mainnet
CACHE_KEY_PREFIX: 'account:'
SIWF_V2_URI_VALIDATION: 'localhost'

services:
redis:
Expand Down
3 changes: 2 additions & 1 deletion env-files/account.template.env
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ SIWF_URL=https://ProjectLibertyLabs.github.io/siwf/v1/ui

# Domain for Sign In With Frequency V2 Login Message
# Required to use SIWF v2
SIWF_V2_DOMAIN=localhost
# This value required to pass e2e tests
SIWF_V2_URI_VALIDATION='https://testnet.frequencyaccess.com'

# Enable debug mode for development
DEBUG=true
Expand Down
Loading

0 comments on commit f1818cf

Please sign in to comment.