专业剑 : ms-customer-authorization

服务描述

该服务主要负责授权和鉴权。鉴权类型AuthType包括SMS OTP,EMAIL OTP,SWK, HWK,FIDO。

主要API

梳理每个API的主要流程和架构,其中使用到的类/接口,表/字段,实体/字段等。

为什么ms-customer-authorization这么复杂?

纵向,某些接口的流程长。大步骤里面小步骤。设计和实现上没有做到高聚合。

横向,在channel和toke type两个维度上扩展。

问题一是耦合在一起,核心功能和非核心功能耦合,不同的业务耦合。

问题二是抽象不够?没有把共用的功能,核心的功能抽象出来。而是大量使用IF ELSE和配置项实现。

有没有合理和不合理的地方?

架构设计的角度,可以优化复杂度吗?

纵向,如果准备阶段的步骤,只和channel有关,考虑分离出去,比如放到ms-customer-authorization-inb实现,互不影响。

另外,这些步骤之间,是否有依赖关系,如果没有,可以使用异步任务实现。

横向,把共用的功能抽取出来放到抽象类实现,或者放到default具体实现类,把个性化的功能放到具体实现类。区分开共用的功能和个性化的功能,进行解耦。

3ds和assisted-channel与其他代码的关系?是否可以剥离出去?把只和channel先关的业务,剥离到domian service实现,而不是放到core service层实现。

/mfa/token/*接口是否与ms-token-mgn功能重合?是否可以剥离?


分析

1、上图中TokenService接口,主要方法包括queryUserProfile()和decideAuthoizationType(),按照Channel维度分别有具体实现类。

queryUserProfile()方法是查询用户信息。查询用户信息包括token信息,但不依赖于token维度。

decideAuthoizationType()方法是计算用户的最终授权类型。需要综合计算用户的可用授权类型、配置授权类型。

因为这两个方法主要依赖于Channel维度,所以用该接口设计实现。

这2个方法真的只依赖于Channel维度?基本上是。

为什么不把prepareRequestDetail()方法,也放入该接口?该方法是获取交易类型TXN Type的配置信息。


2、上图中AuthorizationStrategy接口,实现了2个维度的扩展,分别是Token类型(SMS、Email、SWK、HWK)和Channel(INB、BV、MBSS)。

主要方法包括triggerAuthorization()和validateAuthorization()。

triggerAuthorization()方法用于/request接口的触发消息逻辑,包括SMS、Email、SWK。

和validateAuthorization()方法用于/valildate接口的校验逻辑。

因为这两个方法同时依赖于Token类型和Channel维度,所以使用该接口这种设计实现2个维度的扩展


API /tac/request

1. TwoStepElevationController

控制器层,目前提供了v1/v2/v3的不同版本。

提供了所有的核心功能的接口,包括/tac/request,/tac/validate,access-elevation/request,access-elevation/validate等。

2. TwoStepElevationServiceImpl

服务层,主要方法tacRequest()

根据请求头,创建AuthenticationContext,设置tacRequest,mfaType,challengeCode,triggerOTP,transactionPayload字段。

3. TACRequestServiceImpl

TAC Request流程的具体实现类。

主要方法handle(),包括4个步骤。

预处理:

判断是否passthrough还是standalone,是否pilot user mode。

根据请求传参,设置AuthenticationContext的challengeCodecustomerChallengeCode字段。

根据channel,设置AuthenticationContext的identity字段(决定AuthorizationStrategy接口的具体实现类)。


以下步骤以INBTokenServiceImpl为例

Step1:getUserProfile 获取用户和token信息

通过查询缓存,数据库,调用下游service或SOA等,获取用户信息和用户可用的token信息,并设置AuthenticationContext的authorizerTokenEntity字段(对应AUTHZ_USER_PROFILE表)。

使用了TokenService的具体实现类的queryUserProfile()方法。

涉及查询AUTHZ_USER_PROFILE表。

f1异步调用SOA(UAS-QueryUser)→ iSprint/UAS-QueryUser,获取SWK用户信息,并设置AuthorizerTokensEntity的isprintUserStatusCode,isprintLoginAccountStatusCode,isSwkSvcDown,custom(SWK_LOGIN_MODULE_ID,SWK_BAD_LOGIN_COUNT, TokenStepUpRequestDate, TokenStepUpExpiryDate, AfterCoolingPeriodToVerifyOTP)等字段。

f2异步调用SOA(UAS-QueryToken)→ iSprint/UAS-QueryToken,获取SWK用户soft token信息,并设置AuthorizerTokensEntity的softTokenStatusCode,deviceFingerPrint,deviceOSName,Tid,softTokenSerialNo,pkiTokenSerialNo,custom(SWK_STATUS,USP_COOLING_PERIOD_STARTDT)等字段。

DSSS

f3异步调用SOA(2FA-QueryUser-I)→ DSSS/2FA-QueryUser-I,获取SMS/HWK用户信息,并设置AuthorizerTokensEntity的userStatusCode,custom(DSSS_USER_STATUS,DSSS_OTP_TOKEN_FAILED_TRIES)。废弃。

f3异步调用SOA(2FA-GetTokenReqStatus-I)→ DSSS/2FA-GetTokenReqStatus-I,获取SMS/HWK用户token信息,设置AuthorizerTokensEntity的smsTokenStatusCode,hardTokenStatus,hwkSerialNo,custom(HARD_TOKEN_REQUEST_STATE,)等字段。

f4异步调用SOA(Auth_Inquiry_2FAMediaDestination)→ ?/Auth_Inquiry_2FAMediaDestination,并设置AuthorizerTokensEntity的mobileNo字段。

CAS

f3异步调用ms-token-mgn/internal/tfa/users/retrieve,获取SMS/HWK用户信息。

f4空


f5异步调用INB USP,获取accessCode, tokenType,coolingPeriodStartDt,gracePeriodIndicator,AfterCoolingPeriodToVerifyOTP,AfterCoolingPeriodToVerifyHighRisk。

TBC=====================

Step2:prepareRequestDetail 获取交易类型的配置信息,同步最新数据

通过TXN Type,查询缓存和TAC表,customer_tac表?

这一步骤和TXN Type有关。在TAC Request的流程中有TXN Type,所以有这一步骤。而在2FA Request的流程中没有TXN Type,所以没有这一步骤。

调用SyncTransactionDataService类具体实现类的sync()方法,从Old Car同步最新TAC相关数据。

设置AuthorizationContext的authorizationType,tacEntity,highRiskFlag,extraAuthorizationType,mfaId,transactionType,gtSigningMode,ltSigningMode字段。

更新AUTHZ_POLICY表?

分析:

1、上图中TACRequestServiceImpl类需要用到SysnTransactionDataServcie接口同步数据。

2、SysnTransactionDataServcie接口按照Channel维度分别有不同的具体实现类,不同Channel的同步数据的逻辑不同。


注意:

AUTHZ_POLICY授权类型的配置表,Channel and Auth Type level。

TAC交易类型和授权类型的配置表,Channel and Transaction Type level。

步骤:

1. 通过syncTransactionDataService.syncData(),从old car同步TAC数据。

2. 根据TXN_RULE_MAPPING表设置,校验请求体的必填字段。

3. 判断并设置配置的授权类型initialAuthorizations、TACEntity、HighRiskFlag。


注:该方法逻辑和channel有关,特别是syncTransactionDataService.syncData()。


Step3:decideAuthorizationType 判断(用户可选的)授权类型

使用了TokenService及其具体实现类。

设置AuthorizationContext的requireChallengeCode,availableAuthType,swkFirstLogin,finalAuthorizationType,softTokenStepUpRequired字段。

RegionChannelFlow
SGBV

1. 通过queryUserProfile(),获取用户的可用授权类型数组activeAuthType

2. 从request.preferredToken获取用户的期望的授权类型,可以为空。

3. 按照channel和TxnType,在prepareRequestDetail(),获取配置的授权类型initialAuthorizations=authContext.getAuthorizationType()

3.1 通过DB或bv-snapshot,通过channel和TxnType查询TAC表。

https://bv-snapshot-sg.apps.ocbc.com/v1/tac/{transactionType}/limits

3.2 检查request body的payload。

3.3 检查amount。

4. 在decideAuthorizationType(),比较配置的授权类型initialAuthorizations可用授权类型activeAuthType

得到最终的授权类型FinalAuthType。把activeTokenList转为字符串得到availableAuthType,并返回。



INB

decideAuthorizationType()

1. 获取authContext.highRiskFlag。

2. 根据highRiskFlag,获取signingMode。

3. 如果signingMode=OCRA*,设置authContext.requireChallengeCode=YES。

4. 通过messageService.getMaxCountFromPolicy(),设置authContext.smsMaxCount,从AUTHZ_POLICY表获取短信最大重发次数。

5. 比较配置的授权类型initialAuthorizations和可用授权类型列表activeTokenList,得到最终的授权类型FinalAuthType

6. 如果FinalAuthType=SWK,则activeTokenList只保留SWK。


MBSS

decideAuthorizationTypeV2()


注:以下是BV的版本。

swkFirstTimeLoginCheck() - 检查是否是SWK的FirstTimeLogin。

步骤

如果是2FA流程,而且最终的授权类型FinalAuthType是SWK。

如果authContext.isBvEnableFirstTimeSWKLogin && authorizerTokenEntity.custom.IS_SWK_FIRST_LOGIN。

如果authContext.request.functionCode not in oobSwkInEligibleFunctioncodes。

设置authContext.extraAuthType=SWK。

设置authContext.swkFirstLogin=true。

注:authContext.swkFirstLogin字段受到user level(authorizer.custom.IS_SWK_FIRST_LOGIN)影响。


oobCheck() - 检查是否是SWK的FirstTimeTxn。

步骤:

如果是TAC流程,而且最终的授权类型FinalAuthType是SWK或者TAC.ExtraStepAuth=SWK。

查询authPolicy。

通过请求头deviceInfo.fpt||tid不为空,则为mobile。

如果请求来自mobile,则判断authPolicy.isMobileSWKFirstTimeSignOTP,否则判断authPolicy.isWebSWKFirstTimeSignOTP

如果TAC.FirstTimeSWKTxnCheck为true。

如果authorizer.custom.IS_SWK_FIRST_TXN为true。

设置authContext.swkFirstTxn=true。

注:authContext.swkFirstTxn字段受到user level(authorizer.custom.IS_SWK_FIRST_TXN)、txn level(TAC)、authType level(AUTHZ_POLICY.isWebSWKFirstTimeSignOTP|isMobileSWKFirstTimeSignOTP)共同影响。


decideRequireChallengeCode() - 判断是否需要Challenge Code。

BV版本

步骤:

如果authContext.triggerOTP=YES。

如果mfaType=TAC。

如果最终的授权类型FinalAuthType是SWK且tacSWKOverrideSigningMode=OCRA1) 或HWK且tacHWKOverrideSigningMode=OCRA1)。

设置authContext.requireChallengeCode=YES。

设置overrideFlag。

通过配置文件:tacSWKOverrideSigningMode=OCRA1,tacHWKOverrideSigningMode=OCRA1

不再需要后续判断authContext.highRisk和authPolilcy判断。


如果overrideFlag=true,返回。

如果overrideFlag=false。

如果最终的授权类型FinalAuthType是SWK或者HWK。

如果authContext.highRiskFlag=YES,signingMode=authContext.gtSigningMode。

如果authContext.highRiskFlag=NO,signingMode=authContext.ltSigningMode。

如果signingMode是(NONE,OCRA1,OCRA2)。

设置authContext.requireChallengeCode=YES。


注:以上几个方法都需要用最终的授权类型FinalAuthType判断,所以放在decideAuthorizationType()。


以下是INB的版本,

和BV版本比较,同样有FirstTimeLogin的判断。没有FirstTimeTxn的判断。没有requireChallengeCode的判断。多了是否需要stepup的判断。多了是否在冷静期coolingperiod的判断。

步骤:

1. 获取authContext.highRiskFlag。

2. 根据highRiskFlag,获取signingMode。

3. 如果signingMode=OCRA*,设置authContext.requireChallengeCode=YES。

4. 通过messageService.getMaxCountFromPolicy(),设置authContext.smsMaxCount,从AUTHZ_POLICY表获取短信最大重发次数。

5. 比较配置的授权类型initialAuthorizations可用授权类型列表activeTokenList,得到最终的授权类型FinalAuthType

messageService.verifySpecificAuthzTypeByConfig(authContext, activeTokenList)

messageService.verifySpecificAuthzTypeForOCRA(authContext, activeTokenList)

messageService.verifySpecificAuthzTypeForTOTP(authContext, activeTokenList)

6. 如果FinalAuthType=SWK,则activeTokenList只保留SWK。


checkSWKFirstLogin() - 检查是否是SWK的FirstTimeLogin

如果channel是INB或MBS,mfaType是access-elevation,finalAuthTye是SWK。

如果已经过了冷静期,如果是SG,

如果是INB 共存模式,获取authorizer.custom.afterCoolingPeriodToVerifyOTP

如果不是INB共存模式,获取authorizer.custom.AfterCoolingPeriodToVerifyOTP

设置authContext.swkFirstLogin=afterCoolingPeriodToVerifyOTP|AfterCoolingPeriodToVerifyOTP。

如果是SWK的FirstTimeLogin,设置FinalAuthType=SMS_OTP。activeTokenList和SMS_OTP做交集。


checkSWKFirstCoolingPeriod() - 检查是否过了冷静期

获取authorizer.gracePeriodIndicator。

获取authorizer.custom.coolingPeriodStartDt。如果为空则获取authorizer.tokenStepUpDate。

如果gracePeriodIndicator=false或者coolingPeriodStartDt为空,则返回false,表示不在冷静期。

通过coolingPeriodStartDt和期限配置(15min UAT/12hr Prod),计算是否在冷静期。

如果是SG,如果在冷静期则返回false,如果不在冷静期,则返回true。

如果是MY,如果在冷静期则抛出异常,如果不在冷静期,则返回true。


checkSWKStepUpStatus() - 检查是否是SWK的StepUp状态

如果是TAC,signingMode=OCRA*,FinalAuthType=SWK

获取authorizer.custom.ISprintSWKStatus。

如果ISprintSWKStatus=ACTIVATED,设置authContext.softTokenStepUpRequired=true,并返回。

如果ISprintSWKStatus=ELEVATED而且还在冷静期,抛出异常SWK_IN_COOLING_PERIOD。


INB SWK的状态

安装SWK provision_->未激活->已激活ACTIVATED/未升级->已升级ELEVATED->还在冷静期IN_COOLING_PERIOD->已过冷静期AFTER_COOLING_PERIOD->第一次登录使用FirstTimeLogin设置FinalAuthType=SMS_OTP,发送短信)。


以下是MBSS的版本,

和BV版本的比较,同样有是否需要requireChallengeCode的判断。没有FirstTimeLogin和FirstTimeTxn的判断。

步骤:

1. 获取authContext.highRiskFlag。

2. 根据highRiskFlag,获取signingMode。

3. 如果signingMode=OCRA*,设置authContext.requireChallengeCode=YES。

4. 通过messageService.getMaxCountFromPolicy(),设置authContext.smsMaxCount,从AUTHZ_POLICY表获取短信最大重发次数。

5. 比较配置的授权类型initialAuthorizations和可用授权类型列表activeTokenList,得到最终的授权类型FinalAuthType


messageService.verifySpecificAuthzTypeByConfig(authContext, activeTokenList)


messageService.verifySpecificAuthzTypeForOCRA(authContext, activeTokenList)


messageService.verifySpecificAuthzTypeForTOTP(authContext, activeTokenList)

6. 如果FinalAuthType=SWK,则activeTokenList只保留SWK。


Channel是否是FirstTimeLogin是否是FirstTimeTxn是否需要requireChallengeCode是否需要stepup是否在冷静期coolingperiod
BVYYYNN
INBYNYYY
MBSSNNYNN



Step4:updateAndTrigger 请求授权和发送消息

涉及AUTHZ_TXN表。

调用TriggerServiceImpl类。

调用AuthorizationStrategy及其具体实现类,比如INBSmsTokenServiceImpl类。

发送Kafka消息。

分析:

1、上图中TACRequestServiceImpl类需要用到TriggerServiceImpl接口实现发送数据。

2、TriggerServiceImpl接口按照Channel维度和Token Type维度分别有不同的具体实现类,不同具体实现类的发送数据的逻辑不同。



步骤:

1. 获取authContext.triggerOTP。这个字段从请求体获取,默认为YES。

2. 如果为YES,构建authTxnEntity。

3. 调用triggerService.triggerAuthzService()。

3.1 如果是TAC,而且不是authContext.customChallengeCode=YES,而且authContext.requireChallengeCode=true,而且authTxnEntity.challengeCode=empty,

创建并设置authContext.challengeCode,设置authContext.requireChallengeCode=YES。

3.2 调用AbstractTrigger.triggerAuthorization()

3.2.1 检查是否是重发mfa/otp,如果是重发,检查authType是否变化。如何检查?,对比FinalAuthTYpe和txn.authPolicy.authType

如果没有变化,检查原txn.authStatus,如果是VERIFIED已经验证或者BLOCKED则抛出异常。设置原txn.status=EXPIRED,创建新的txn记录,同下。

如果有变化,设置原txn.status=EXPIRED,创建新的txn记录(从原txn记录复制字段,包括createdBy、updatedBy)。ver+1,retryCount+1,authCount=0。如果原txn.isCustomChallengeCode=true,则直接复制原txn.challengeCode。否则重新创建challengeCode。

3.2.2 设置新txnEntity。

3.2.3 调用authorizationStrategy.triggerAuthorization()

如果是HardToken,不需要cust-auth发送什么信息。

如果是SMS OTP,

Channel创建OTP发送OTP
BVInvoke SSOInvoke SSO
MBSSStandalone

Invoke ms-cust-noti

INBInvoke ms-token-mgnInvoke ms-cust-noti

SMS OTP

Channel创建OTP发送OTP
INBSOAms-customer-notification
BVSSOSSO
MBSSstandalonems-customer-notification

如果是Email OTP,

Channel创建OTP发送OTP
INBStandalonems-customer-notification
BVStandalonems-customer-notification
MBSSStandalonems-customer-notification


如果是SoftToken,

Channel检查coolingPeriod检查SWKFirstTimeLogin检查SWKFirstTimeTxn检查shadowMode备注
INBYNNY只是在这个步骤没有检查FirstTimeLogin和FirstTimeTxn。NotificationType=PUSH_OTP
BVNYYYNotificationType=EMAIL/PUSH_OTP_BV
MBSSYNYN为什么没有发送信息?


=========TBC


分析:

1. 实际上,Step1/2/3都是为了step4的准备动作,而Step4是核心功能:请求(发送)。

2. 因为TACRequest和2FARequest都有类似的准备动作,所以抽象出了AbstractRequest类。

3. 这些准备动作当中,比如queryUserProfile()和decideAuthorizationType(),同具体的channel有关,和具体的token type无关

所以通过接口TokenService及其具体实现类,比如在INBTokenServiceImpl类等实现具体逻辑。

4. 接口TokenService

该接口提供了2个方法:queryUserProfile()和decideAuthorizationType()。

该接口有4个具体实现类:INBTokenServiceImpl,BVTokenServiceImpl,MBSSTokenServiceImpl,OSPLTokenServiceImpl。

注意:该接口在channel维度进行了扩展,而没有在token type维度进行扩展。

因为不同的token type,在获取用户信息和判断授权类型上没有区别?

4.1 INBTokenServiceImpl

用到4张表:

TokenPreferenceRepository,调用inb数据库的存储过程

uthorizerTokenRepository,调用AUTHZ_USER_PROFILE

TokenTypePreferenceRepository,调用cust_token_preference表

UserSmsRepository,调用USER_SMS_RECORD表

4.2 BVTokenServiceImpl

用到1张表:AuthorizerTokenRepository,调用AUTHZ_USER_PROFILE表

4.3 MBSSTokenServiceImpl

用到1张表:AuthorizerTokenRepository,调用AUTHZ_USER_PROFILE表

4.4 OSPLTokenServiceImpl

用到1张表:AuthorizerTokenRepository,调用AUTHZ_USER_PROFILE表


5. 接口AuthorizationStrategy

该接口提供了2个方法(核心功能):triggerAuthorization()和validateAuthorization()。

该接口有4个实现类:SMSAuthorizationServiceImpl,EmailAuthorizationServiceImpl,SoftTokenAuthorizationServiceImpl,HardTokenAuthorizationServiceImpl。

而且,每个实现类都关联了一个接口,比如SMSAuthorizationServiceImpl关联了SMSService接口。


通过AuthorizationStrategy接口实现了token type维度的扩展。

不论哪种token,都需要有请求(发送)和校验的核心功能。


通过XXXService接口,又实现了channel维度的扩展。

因为任何一种token,对应到不同的channel,请求(发送)和校验的功能的具体实现又存在不同。

通过这种设计,实现了2个维度的扩展。

问题:在SMSAuthorizationServiceImpl类的triggerAuthorization()方法具体逻辑,和INBSmsTokenServiceImpl类的requestSMSOTP()方法的具体逻辑,有什么不同?

SMSAuthorizationServiceImpl类的triggerAuthorization()方法的功能简单,主要是根据channel设置AuthorizationContext的identity字段,决定SMSService接口的具体实现类,比如INBSmsTokenServiceImpl类。

在INBSmsTokenServiceImpl类的requestSMSOTP()方法,实现具体逻辑。

=========TBC

5.1. 接口SMSService

该接口提供了2个方法:requestSMSOTP(),validateSMSOTP()。

该接口有多个实现类:INBSmsTokenServiceImpl,BVSmsTokenServiceImpl,MBSSSmsTokenServiceImpl,DefaultSmsTokenServiceImpl。

INBSmsTokenServiceImpl这个类复杂吗?

5.2. 接口SoftTokenService

该接口提供了3个方法:handleRequest(),handleValidate(),checkIfActive()

该接口有多个实现类:INBSoftTokenServiceImpl,BVSoftTokenServiceImpl,MBSSSoftTokenServiceImpl,DefaultSoftTokenServiceImpl。

5.3. 接口HardTokenService

该接口提供了2个方法:hardTokenRequest(),hardTokenValidate()

该接口有多个实现类:INBHardTokenServiceImpl,BVHardTokenServiceImpl,MBSSHardTokenServiceImpl,DefaultHardTokenServiceImpl。

6. TriggerServiceImpl

主要完成request核心功能之前的准备工作,并调用AuthorizationStrategy的具体实现类实现核心功能。

1. preHandle()

判断是否是passthrough。

2. triggerAuthorization()

具体执行trigger服务。实际上是由AuthorizationStrategy的具体实现类(SMSAuthorizationServiceImpl,EmailAuthorizationServiceImpl,SoftTokenAuthorizationServiceImpl,HardTokenAuthorizationServiceImpl)的triggerAuthorization()方法实现。

triggerAuthorization()方法调用了SMSService接口的具体实现类,比如INBSMSServiceImpl类的requestSMSOTP()方法。

requestSMSOTP()方法调用了NotificationService接口的具体实现类,比如CustomerNotificationServiceImpl类的send()方法。

send()方法的具体实现包括以下子方法:

prepareDataAndDoSend()

preparePayloadParam()

addAdditionalParam()

prepareNotificationData()

最终调用sdk提供的clientNotificationSdkProcessor类的processNotification()方法发送消息。

3. triggerAuthzService()

处理发送消息给用户,包括SMS,EMAIL,SWK。实际调用抽象父类AbstractTrigger的triggerAuthorization()。

4. handle()

调用triggerAuthzService()方法。

5. checkForAuthTypeChannel()

比较authorization type,选择最优先。


响应

字段值 - ACCESS_ELEVATION值 - TAC值 - GENERIC
triggerOTPauthContext.triggerOTP
requireChallengeCodeauthContext.requireChallengeCode
authIdupdateAuthTxnEntity.Id
txnIdupdateAuthTxnEntity.txnId
statusELEVATION_REQUIREDTAC_REQUIREDNA
authReferenceupdateAuthTxnEntity.authReference
challengeCodeupdateAuthTxnEntity.challengeCode

TxnUtil.identifyOOBChallengeCode(authContext.channel, updateAuthTxnEntity)

如果是BV而且是firstTimeTxn,返回null,因为challengeCode会通过Email发送给用户。

否则返回updateAuthTxnEntity.challengeCode。

NA
notifySoftTokentriggerAuthResult.notifySoftTokenNA
authTypeCodeupdateAuthTxnEntity.authPolicy.authType

最终授权类型

availableAuthTypeCode

authTxnEntity.availableAuthTypeCodeNA
maskedMobileNoupdateAuthTxnEntity.maskedMobileNoNA
maskedEmailAddressupdateAuthTxnEntity.maskedEmailAddressNA
tokenstokensNA
isSWKFirstLoginauthContext.isSwkFirstLoginNANA
isSWKFirstTxnNA

if(includeSWKFirstTxnChannels=empty || not includeSWKFirstTxnChannels.contains(channel)) {
isSWKFirstTxn = authTxnEntity.authPolicy.authType is SWK && authContext.isSwkFirstTxn;
}
includeSWKFirstTxnChannels = MBSS, MBSS_MO

// 如果channel是MBSS, MBSS_MO,按照authContext.isSwkFirstTxn返回。

// 否则,按照authTxnEntity.authPolicy.authType is SWK && authContext.isSwkFirstTxn返回。

NA
sendSuccessauthContext.pushNotificationStatus
oAuthDetails

authContext.requestHeader.accOp

authContext.requestHeader.refOp

NANA
userRemainingCountauthContext.userRemainingCountNA
auditLogDTONAauthContext.auditLogDTONA

注:tokens字段

boolean includeTokenSerialNo = isTsnNotifySoftToken ? triggerAuthResult.notifySoftToken == NO_SENDING : authContext.shadowDeviceModeFlag=YES;

List<TokensResponse> tokens = includeTokenSerialNo && authContext.finalAuthType=SWK ? updatedAuthTxnEntity.tokenSerialNo : null;

// 如果isTsnNotifySoftToken = true,是否返回tokenSerialNo由notifySoftToken决定,如果没发送消息NO_SENDING,则可能返回tokens,否则如果已经发送消息,则不返回tokens。

// 如果是false,表示由shadowDeviceMode决定。如果是shadowDevice,则返回tokens,否则不返回。

// sg,my,cn,hk是true

// corpsg,corpmy是false

// 如果最终的授权类型FinalAuthType是SWK,而且没有发送应用消息或者是shadow device,则返回tokenSerialNo。

 


API /tac/validate

1. TACValidateServiceImpl

主要方法handle(),包括3个步骤。

step1,查询AUTHZ_TXN表。

校验txnId是否一致。

step2,根据AUTHZ_REF_CHECK配置和authz_policy

校验随机码authReference。

step3,使用TxnUtl类checkDurationFlow()


在校验之前检查authz_status。

通过MessageServieImpl类的validatePreConditionsAndThrow(),进行预校验。

step4,使用了AuthorizationStrategy及其具体实现类

调用validateAuthorization()方法进行校验。

如果是Email,Standalone。

如果是HardToken,

Channel校验
BVInvoke SSO verifyHWKTAC/verifyHWKOTP
INBInvoke ms-token-mgn/tfa/otp/verify
MBSSInvoke ms-token-mgn/tfa/otp/verify

如果是SMS,

Channel校验
BVInvoke SSO 2FA-VerifyOTP-I
INBInvoke ms-token-mgn/tfa/otp/verify
MBSSInvoke ms-token-mgn/tfa/otp/verify

如果是SoftToken,

Channel校验
BVInvoke SSO verifySoftTokenTAC/verifySoftTokenOTP
INBInvoke SOA UAS-VerifyOTP
MBSSInvoke ms-token-mgn/soft-tokens/otp/validate


比如BVSoftTokenServiceImpl具体实现类的handleValidate()方法,调用SSOServiceFacade的verifyOTPByMFAType(),调用SSOSWKService类的verifyTAC(),调用SSOIntegrationClient类的verifySoftTokenTAC()进行校验。

实际上,SSOIntegrationClient类实现了各种校验,包括SSOSWKService,SSOHWKService,SSOSMSService。

注:SoftToken、HardToken、SMS都需要先调用tokenService.queryUserProfile()。

step5,根据校验结果,更新AUTHZ_TXN表。

如果校验成功,更新authTxn.authStatus=VERIFIED。检查是否需要触发multi step流程。

如果校验失败,更新authTxn.authCount+1。


2. MessageServiceImpl

提供了validatePreConditionsAndThrow()方法,在调用validate核心功能之前,检查状态和校验。

API /otp/request

这个API,对于不同的channel有区别吗?基本没有。

通过GenericElevationServiceImpl类的genericOTPAndSendSMSorNotification()创建OTP,并发送(SMS,Email)。

step1,通过请求头request.mobileNo是否为空,判断是通过SMS还是Email发送OTP。

step2,通过MessageServiceImplisEligibleForTriggering(),判断当前的AUTHZ_STS_CD状态字段,是否有资格/允许发送OTP。

step3,通过GenericUtilitygenerateOTP(),产生OTP,并计算hash值。产生参考码AuthReference,是否有条件?

step4,通过AuthorizationPolicyCache类从本地缓存或者AUTHZ_POLICY表查询配置,设置OTP的有效时长。

step5,更新AUTHZ_TXN表。

step6,通过NotificationServicesend()方法,发送消息。实际通过ms-customer-notification-sdk


API /otp/validate

通过GenericElevationServiceImpl类的genericOTPValidateV2()校验OTP。

step1,通过请求头的auth ID,查询AUTHZ_TXN表。

step2,预校验。校验请求头字段和表记录是否一致,包括transactionID,参考码。

step3,通过比较请求头的otp和表记录的otp,进行hash校验。

step4,根据校验结果,更新AUTHZ_TXN表。

API /otp/daily-limit-check

通过GenericElevationServiceImpl类的getDailyLimitStatus()方法。

该方法查询AUTHZ_TXN表,通过channel + transaction Id。

返回结果NEW OTP,LIMIT EXCEED,WITHIN DAILY LIMIT。

API /v3/access-elevation/request

1. AccessElevationRequestServiceImpl

主要方法handle(),包括?个步骤。

预处理

判断是否passthrough。

通过AccessElevationCache类从本地缓存或者ACCESS_ELEVATION表查询相关配置。

校验jwtDomain

校验是否需要提权,比较表字段ial和请求头amr字段。

Step1,获取用户的可用token列表

通过TokenService具体实现类的queryUserProfile(),查询缓存或SOA。

Step2:decideAuthorizationType 判断(用户可选的)授权类型

使用了TokenService及其具体实现类的decideAuthorizationType()。

通过AuthorizationPolicyCache类从本地缓存或者AUTHZ_POLICY表查询配置

Step3:updateAndTrigger 请求授权和发送消息

如果triggerOTP为Y,通过TriggerServiceImpl类的triggerAuthzService(),实际上通过AuthorizationStrategy的具体实现类的triggerAuthorization()实现。


API /v3/access-elevation/validate

1. AccessElevationValidateServiceImpl

step1,通过请求头的auth ID,查询AUTHZ_TXN表。
step2,预校验。校验当前状态。

通过MessageServieImpl类的validatePreConditionsAndThrow,进行预校验。

step3,使用了AuthorizationStrategy及其具体实现类validateAuthorization()
step4,校验后处理

如果校验成功,更新AUTHZ_TXN表状态字段,并发送kafka消息。

如果校验失败,更新AUTHZ_TXN表失败次数字段,并抛出异常。

API /internal/access-elevation/shadow/validate

步骤

1. 通过channel和fn=ELEVATION查询access-elevation。

2. 通过channel和AuthType=SWK,查询auth_policy。

3. 如果request.otpValidationResult=SUCCESS,skip UAS otp validation invoke,并返回。

4. 如果不是shadowValidate,则需要调用tokenService.queryUserProfile()。

5. 复用调用softTokenService.handleValidate()。

Mobile - Post login
A1. Cust sec will call GET /mfa/token/preference to cust authz to check what token user have
A2. If user swk in cooling period of past cooling period but oob not done, cust sec will return indicator to mobile saying swk not ready, go to A3.1 otherwise go with A3.2

A3.1. If swk not ready, Mobile will repeat B1 and B2 for sms otp

A3.2. If swk is ready, cust sec will call internal/shadow-elevatiom/validate api to complete swk otp flow

mfe post login
B1. Mfe calls POST /access-elevation/request
B2. Mfe or mobile calls POST /access-elevation/validate


Mobile - Post login

A1. cust-sec → cust-auth/mfa/token/preference GET,

返回List<String> availableOptions(比如[SWK, HWK, SMS_OTP]),

List<ActivatedToken> activatedOptions(isCoolingPeriod, afterCoolingPeriodToVerifiedOtp, coolingPeriodRemaining, tokenStepUpRequest, tokenStepUpExpiry, deviceInfo, coolingPeriodIndicator)

如果用户的SWK在cooling period,但是OOB没有完成,返回mobile:SWK not ready,走A2.1。否则走A2.2

A2.1 如果SWK not ready,重复B1和B2,sms otp。

A2.2 如果SWK is ready,cust-sec → cust-auth/internal/shadow/validate, 完成SWK OTP flow。

otpValidationResult=SUCCESS, no validate anymore。


MFE - Post Login

B1. MFE → POST /access-elevation/request

B2. MFE or Mobile -> POST /access-elevation/validate


cust-sec → iSprint validate SWK

after cooling period, if first time txn

cust-sec → cust-auth/2fa/request


mb → cust-auth/2fa/validate 

if not first time txn, means oob is done

if swk available, and oob is done

cust-sec → cust-auth/shadow/validate, otpValidationResult=SUCCESS, no validate anymore


AuthenticationContext 上下文类

accessElevationRequest
请求体中access-elevation/request

accessElevationValidate
请求体中access-elevation/validate

authorizationElevationRequest

authorizerTokenEntity
存储用户信息user profile,对应AUTHZ_USER_PROFILE表

challengeCode
产生的ChallengeCode,或者请求的传参。
customerChallengeCode
请求传参是否包含challengeCode。

mfaType

tacRequest
请求体

tacValidate
请求体


triggerOTP
是否触发OTP

transactionPayload
请求体

CommonContext

auditModel
channel
headers
tokenContext
存储token信息,来源于jwt

tokenContext.custom

API /mfa/login-mode

暂未实现。

API /mfa/otp

实际上通过TriggerServiceImplhandle()方法,其余参考/tac/request。

API GET /mfa/token/preference

通过ITokenPreferenceService的具体实现类query()方法,

以下以INBTokenPreferenceServiceImpl为例。

AUTHZ_USER_PROFILE

1. 通过TokenService的具体实现类的queryUserProfile()方法,查询缓存或SOA,获取user profile,并设置isSwkSvcDown字段。

在co-existence模式下,通过INB USP同步数据,获取INB token preference,并入库cust_token_preference表。

返回体包括:displayPreference,defaultSelection,availableOptions,activatedOptions,userProfiles,isSwkSvcDown等字段。

在非co-existence模式下,不需要通过INB USP同步数据,通过查询本地cust_token_preference表处理。

2. 获取并设置activatedOptions列表字段。

根据availableOptions列表和status字段,获取activatedOptions列表,包括tokenType,swkStatus,status,swkStepUpEligibility,mobileNo字段。

如果是SWK,设置isCoolingPeriod,deviceInfo(包括tsn,tid,fpt),tokenStepUpRequestDate,tokenStepUpExpiryDate字段。

如果是HWK,设置deviceInfo(包括tsn)。

API GET /tac/limits

如果请求参数不包括transaction type list,通过TACServiceImpl类,同步调用SOA的EBS-TransSigningLimit-I接口,获取结果列表,包括每个transaction type对应的limit。

如果请求参数包括,

获取EBS的配置项。

通过TACServiceImpl类,异步调用SOA的EBS-TransSigningLimit-I接口。

通过TACServiceImpl类,查询数据库TAC表,channel为INB。

最后把得到的transaction type list合并。

API /tac/authorization-status/retrieve

通过请求头判断channel。

通过TACServiceImpl类status()方法,判断是否passthrough模式,查询AUTHZ_TXN表。

校验记录是否过期。

校验是否多步骤交易签名。返回SUCCESS(所有记录状态都是VERIFIED),FAILED(任何一步是BLOCKED),PENDING(其他情况)。


主要业务流程

主要架构设计方案

主要数据库设计

遇到的问题和解决方案

优缺点和改进方案分析,业界对比


Attachments:

image2023-9-19_22-40-38.png (image/png)
image2023-9-24_23-25-11.png (image/png)
image2023-9-26_23-18-37.png (image/png)
image2023-9-26_23-25-24.png (image/png)
image2023-9-26_23-29-40.png (image/png)
ms-customer-authorization2.pdf (application/pdf)
image2023-10-5_11-45-26.png (image/png)
IMG_0302.jpeg (image/jpeg)
IMG_0301.jpeg (image/jpeg)
IMG_0300.jpeg (image/jpeg)
IMG_0299.jpeg (image/jpeg)
IMG_0298.jpeg (image/jpeg)
IMG_0297.jpeg (image/jpeg)
IMG_0296.jpeg (image/jpeg)
IMG_0295.jpeg (image/jpeg)
IMG_0294.jpeg (image/jpeg)
IMG_0293.jpeg (image/jpeg)
IMG_0304.jpeg (image/jpeg)
IMG_0303.jpeg (image/jpeg)