Skip to content

Commit bcfd8bf

Browse files
committed
feat: add backend feature flag for auth redirect and form retrieval
1 parent 39c67fb commit bcfd8bf

File tree

3 files changed

+85
-46
lines changed

3 files changed

+85
-46
lines changed

src/app/modules/form/public-form/public-form.controller.ts

Lines changed: 73 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import {HttpStatusCode} from 'axios'
2-
import {celebrate, Joi, Segments} from 'celebrate'
3-
import {StatusCodes} from 'http-status-codes'
4-
import {err, ok, Result} from 'neverthrow'
1+
import { HttpStatusCode } from 'axios'
2+
import { celebrate, Joi, Segments } from 'celebrate'
3+
import { StatusCodes } from 'http-status-codes'
4+
import { err, ok, Result } from 'neverthrow'
55

6-
import {featureFlags} from '../../../../../shared/constants'
6+
import { featureFlags } from '../../../../../shared/constants'
77
import {
88
ErrorCode,
99
ErrorDto,
@@ -17,37 +17,48 @@ import {
1717
PublicFormViewDto,
1818
StrippedFormWorkflowDto,
1919
} from '../../../../../shared/types'
20-
import {stripWorkflowEmails} from '../../../../../shared/utils/strip-workflow-emails'
21-
import {IPopulatedMultirespondentForm} from '../../../../types'
22-
import {createLoggerWithLabel} from '../../../config/logger'
23-
import {isMongoError} from '../../../utils/handle-mongo-error'
24-
import {createReqMeta, getRequestIp} from '../../../utils/request'
25-
import {getFormIfPublic} from '../../auth/auth.service'
20+
import { stripWorkflowEmails } from '../../../../../shared/utils/strip-workflow-emails'
21+
import { IPopulatedMultirespondentForm } from '../../../../types'
22+
import { createLoggerWithLabel } from '../../../config/logger'
23+
import { isMongoError } from '../../../utils/handle-mongo-error'
24+
import { createReqMeta, getRequestIp } from '../../../utils/request'
25+
import { getFormIfPublic } from '../../auth/auth.service'
2626
import * as BillingService from '../../billing/billing.service'
27-
import {ControllerHandler} from '../../core/core.types'
28-
import {MyInfoData} from '../../myinfo/myinfo.adapter'
27+
import { ControllerHandler } from '../../core/core.types'
28+
import { MyInfoData } from '../../myinfo/myinfo.adapter'
2929
import {
3030
MYINFO_AUTH_CODE_COOKIE_NAME,
3131
MYINFO_AUTH_CODE_COOKIE_OPTIONS,
3232
MYINFO_LOGIN_COOKIE_NAME,
3333
MYINFO_LOGIN_COOKIE_OPTIONS,
3434
} from '../../myinfo/myinfo.constants'
35-
import {MyInfoService} from '../../myinfo/myinfo.service'
36-
import {createMyInfoLoginCookie, extractAuthCode, getMyInfoEserviceIdInForm,} from '../../myinfo/myinfo.util'
37-
import {SGIDMyInfoData} from '../../sgid/sgid.adapter'
35+
import { MyInfoService } from '../../myinfo/myinfo.service'
36+
import {
37+
createMyInfoLoginCookie,
38+
extractAuthCode,
39+
getMyInfoEserviceIdInForm,
40+
} from '../../myinfo/myinfo.util'
41+
import { SGIDMyInfoData } from '../../sgid/sgid.adapter'
3842
import {
3943
SGID_CODE_VERIFIER_COOKIE_NAME,
4044
SGID_COOKIE_NAME,
4145
SGID_MYINFO_COOKIE_NAME,
4246
SGID_MYINFO_LOGIN_COOKIE_NAME,
4347
} from '../../sgid/sgid.constants'
44-
import {SgidInvalidJwtError, SgidMalformedMyInfoCookieError, SgidVerifyJwtError,} from '../../sgid/sgid.errors'
45-
import {SgidService} from '../../sgid/sgid.service'
46-
import {validateSgidForm} from '../../sgid/sgid.util'
47-
import {InvalidJwtError, VerifyJwtError} from '../../spcp/spcp.errors'
48-
import {getOidcService} from '../../spcp/spcp.oidc.service'
49-
import {getRedirectTargetSpcpOidc, validateSpcpForm,} from '../../spcp/spcp.util'
50-
import {generateHashedSubmitterId} from '../../submission/submission.utils'
48+
import {
49+
SgidInvalidJwtError,
50+
SgidMalformedMyInfoCookieError,
51+
SgidVerifyJwtError,
52+
} from '../../sgid/sgid.errors'
53+
import { SgidService } from '../../sgid/sgid.service'
54+
import { validateSgidForm } from '../../sgid/sgid.util'
55+
import { InvalidJwtError, VerifyJwtError } from '../../spcp/spcp.errors'
56+
import { getOidcService } from '../../spcp/spcp.oidc.service'
57+
import {
58+
getRedirectTargetSpcpOidc,
59+
validateSpcpForm,
60+
} from '../../spcp/spcp.util'
61+
import { generateHashedSubmitterId } from '../../submission/submission.utils'
5162
import {
5263
AuthTypeMismatchError,
5364
FormRespondentNotWhitelistedError,
@@ -57,7 +68,7 @@ import {
5768
import * as FormService from '../form.service'
5869

5970
import * as PublicFormService from './public-form.service'
60-
import {mapFormAuthError, mapRouteError} from './public-form.utils'
71+
import { mapFormAuthError, mapRouteError } from './public-form.utils'
6172

6273
const logger = createLoggerWithLabel(module)
6374

@@ -117,7 +128,7 @@ export const handleGetPublicForm: ControllerHandler<
117128
const form = formResult.value
118129
const publicForm = form.getPublicView() as PublicFormDto
119130

120-
const { authType } = form
131+
let authType = form.authType
121132
const isIntranetUser = FormService.checkIsIntranetFormAccess(
122133
getRequestIp(req),
123134
form,
@@ -127,6 +138,14 @@ export const handleGetPublicForm: ControllerHandler<
127138
return res.json({ form: publicForm, isIntranetUser })
128139
}
129140

141+
if (req?.growthbook?.isOn(featureFlags.useFormsgMyinfo) && [
142+
FormAuthType.SP,
143+
FormAuthType.SGID,
144+
FormAuthType.SGID_MyInfo,
145+
].includes(authType)) {
146+
authType = FormAuthType.MyInfo
147+
}
148+
130149
let spcpSession
131150
let myInfoFields
132151

@@ -657,29 +676,43 @@ export const _handleFormAuthRedirect: ControllerHandler<
657676
// NOTE: Using retrieveFullForm instead of retrieveForm to ensure authType always exists
658677
return FormService.retrieveFullFormById(formId)
659678
.andThen((form) => {
660-
if (req.growthbook?.isOn(featureFlags.useFormsgMyinfo)) {
661-
form.authType = FormAuthType.MyInfo
679+
if (
680+
req.growthbook?.isOn(featureFlags.useFormsgMyinfo) &&
681+
[FormAuthType.SP, FormAuthType.SGID, FormAuthType.SGID_MyInfo].includes(
682+
form.authType,
683+
)
684+
) {
685+
logger.info({
686+
message: `Using FormSG MyInfo flag, setting authType to MyInfo`,
687+
meta: {
688+
previousAuthType: form.authType,
689+
forceAuthType: FormAuthType.MyInfo,
690+
...logMeta,
691+
},
692+
})
693+
formAuthType = FormAuthType.MyInfo
694+
} else {
695+
formAuthType = form.authType
662696
}
663697
return ok(form)
664698
})
665699
.andThen((form) => {
666-
formAuthType = form.authType
667-
const useFormsgEsrvcId = req.growthbook?.isOn(
668-
featureFlags.useFormsgEsrvcId,
669-
)
670-
switch (form.authType) {
700+
switch (formAuthType) {
671701
case FormAuthType.MyInfo:
672-
return getMyInfoEserviceIdInForm(form, useFormsgEsrvcId).andThen(
673-
([form, eserviceId]) =>
674-
MyInfoService.createRedirectURL({
675-
formEsrvcId: eserviceId,
676-
formId,
677-
requestedAttributes: form.getUniqueMyInfoAttrs(),
678-
encodedQuery,
679-
}),
702+
logger.info({ message: 'MyInfo form', meta: logMeta })
703+
return getMyInfoEserviceIdInForm(form, {
704+
growthbook: req.growthbook,
705+
}).andThen(([form, eserviceId]) =>
706+
MyInfoService.createRedirectURL({
707+
formEsrvcId: eserviceId,
708+
formId,
709+
requestedAttributes: form.getUniqueMyInfoAttrs(),
710+
encodedQuery,
711+
}),
680712
)
681713
case FormAuthType.SP: {
682714
return validateSpcpForm(form).asyncAndThen((form) => {
715+
logger.info({ message: 'SP form', meta: logMeta })
683716
const target = getRedirectTargetSpcpOidc(
684717
formId,
685718
FormAuthType.SP,

src/app/modules/myinfo/myinfo.controller.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { celebrate, Joi, Segments } from 'celebrate'
22
import { StatusCodes } from 'http-status-codes'
33

4-
import { featureFlags } from '../../../../shared/constants'
54
import { Environment } from '../../../types'
65
import config from '../../config/config'
76
import { createLoggerWithLabel } from '../../config/logger'
@@ -48,9 +47,10 @@ export const respondWithRedirectURL: ControllerHandler<
4847
{ formId: string; encodedQuery?: string }
4948
> = async (req, res) => {
5049
const { formId, encodedQuery } = req.query
51-
const useFormsgEsrvcId = req.growthbook?.isOn(featureFlags.useFormsgEsrvcId)
5250
return FormService.retrieveFormById(formId)
53-
.andThen((form) => getMyInfoEserviceIdInForm(form, useFormsgEsrvcId))
51+
.andThen((form) =>
52+
getMyInfoEserviceIdInForm(form, { growthbook: req.growthbook }),
53+
)
5454
.andThen(([form, eserviceId]) =>
5555
MyInfoService.createRedirectURL({
5656
formEsrvcId: eserviceId,

src/app/modules/myinfo/myinfo.util.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { GrowthBook } from '@growthbook/growthbook'
12
import bcrypt from 'bcrypt'
23
import { StatusCodes } from 'http-status-codes'
34
import jwt from 'jsonwebtoken'
@@ -6,6 +7,7 @@ import mongoose, { FlattenMaps } from 'mongoose'
67
import { err, ok, Result } from 'neverthrow'
78
import { v4 as uuidv4, validate as validateUUID } from 'uuid'
89

10+
import { featureFlags } from '../../../../shared/constants'
911
import {
1012
myInfoCountries,
1113
myInfoDialects,
@@ -38,6 +40,7 @@ import {
3840
} from '../../../types'
3941
import { spcpMyInfoConfig } from '../../config/features/spcp-myinfo.config'
4042
import { createLoggerWithLabel } from '../../config/logger'
43+
import Growthbook from '../../loaders/express/growthbook'
4144
import { DatabaseError } from '../core/core.errors'
4245
import {
4346
AuthTypeMismatchError,
@@ -316,15 +319,18 @@ export const createRelayState = (
316319
/**
317320
* returns form with an e-service ID if form is a MyInfo form
318321
* @param form Form to validate
322+
* @param ctx Optional context containing request information
319323
*/
320324
export const getMyInfoEserviceIdInForm = <T extends IFormSchema>(
321325
form: T,
322-
useFormsgEsrvcId?: boolean,
326+
ctx?: { growthbook?: GrowthBook },
323327
): Result<
324-
[MyInfoForm<T>, string],
328+
[MyInfoForm<T> | IFormSchema, string],
325329
FormAuthNoEsrvcIdError | AuthTypeMismatchError
326330
> => {
327-
if (isMyInfoForm(form)) {
331+
const useFormsgEsrvcId = ctx?.growthbook?.isOn(featureFlags.useFormsgEsrvcId)
332+
const useFormsgMyinfo = ctx?.growthbook?.isOn(featureFlags.useFormsgMyinfo)
333+
if (isMyInfoForm(form) || useFormsgMyinfo) {
328334
const esrvcId = useFormsgEsrvcId
329335
? spcpMyInfoConfig.spEsrvcId
330336
: form.esrvcId || spcpMyInfoConfig.spEsrvcId

0 commit comments

Comments
 (0)