Table of Contents
Overview
Recently I had the requirement to publish the /manageotp Citrix ADC Native OTP Selfservice-Portal to external with the following filtering:
- If User hasn’t enrolled any OTP yet, log into Selfservice should be Username + Password
- If User has already enrolled any OTP, log into Selfservice should be Username + Password and next up native OTP
During this configuration I came up with a strange issue called “Replay of OTP code detected. Please use new code.” which I will give you some insights and the solution to fix this.
Configuration
First of all, I’m always seperating the Selfservice-Portal FQDN from any productive Gateway / AAA vServers, so Users aren’t able to call /manageotp by manual.
The nFactor Flow will be as the following:
First LoginSchema is the built-in SingleAuthManageOTP.xml which shows Username + Password. During Login we’re doing a NoAuth extraction to put the Active Directory OTP-Attribute in an AAA.USER.ATTRIBUTE to filter if the next Schema is requesting OTP – or not.
add authentication ldapAction LDAPS-Domain_noauth_CheckMFAEnrollment -serverIP 10.10.10.10 -serverPort 636 -ldapBase "dc=contoso,dc=com" -ldapBindDn
se*****@co*****.com
-ldapBindDnPassword -ldapLoginName sAMAccountName -groupAttrName memberOf -subAttributeName cn -secType SSL -authentication DISABLED -nestedGroupExtraction ON -maxNestingLevel 5 -groupNameIdentifier cn -groupSearchAttribute memberOf -groupSearchSubAttribute cn -Attribute5 otptoken
#Non-Encrypted OTP
add authentication Policy LDAPS-Only-NoMFAEnrolled -rule "AAA.USER.ATTRIBUTE(5).CONTAINS(\"#@\").NOT" -action NO_AUTHN
#Encrypted OTP
add authentication Policy LDAPS-Only-NoMFAEnrolled -rule "AAA.USER.ATTRIBUTE(5).CONTAINS({\"otpdata\").NOT" -action NO_AUTHN
There weren’t any issues for Users creating their first OTP, as Username + Password is working fine.
For Users trying to login with their newly created OTP, the second LoginSchema “OnlyOTP” shows up, but the OTP never be accepted with the error “Replay of OTP code detected. Please use new code.”
During debugging I realized the OTP gets accepted, but ADC isn’t accepting to show the Manageotp page, as the /manageotp NSC_TASS Cookie gets lost. I tried several things to re-insert the Cookie, it never worked accurate as the built-in Manageotp-Method isn’t designed to use divers LoginSchema in series.
I came across a simple idea. The built-in SingleAuthManageOTP.xml has hidden fields that enable the /manageotp Selfservice web page after authentication, these are the following lines:
<Requirement><Credential><ID>otpmanage</ID><Type>otpmanage</Type></Credential><Input><Text><Secret>false</Secret><Hidden>true</Hidden><InitialValue>1</InitialValue><Constraint>.+</Constraint></Text></Input></Requirement> <Requirement><Credential><ID>pushregister</ID><Type>nsg_registerpush</Type></Credential><Input><Text><Secret>false</Secret><Hidden>true</Hidden><InitialValue>1</InitialValue><Constraint>.+</Constraint></Text></Input></Requirement> <Requirement><Credential><ID>otpregister</ID><Type>nsg_registerotp</Type></Credential><Input><Text><Secret>false</Secret><Hidden>true</Hidden><InitialValue>1</InitialValue><Constraint>.+</Constraint></Text></Input></Requirement>
I ended up creating a new OnlyOTP.xml which includes these lines, too:
<?xml version="1.0" encoding="UTF-8"?> <AuthenticateResponse xmlns="http://citrix.com/authentication/response/1"> <Status>success</Status> <Result>more-info</Result> <StateContext/> <AuthenticationRequirements> <PostBack>/nf/auth/doAuthentication.do</PostBack> <CancelPostBack>/nf/auth/doLogoff.do</CancelPostBack> <CancelButtonText>Cancel</CancelButtonText> <Requirements> <Requirement><Credential><ID>passwd</ID><SaveID>ExplicitForms-Password</SaveID><Type>password</Type></Credential><Label><Text>OTP Code</Text><Type>nsg-login-label</Type></Label><Input><Text><Secret>true</Secret><ReadOnly>false</ReadOnly><InitialValue/><Constraint>.+</Constraint></Text></Input></Requirement> <Requirement><Credential><Type>none</Type></Credential><Label><Text>Please insert your OTP Code</Text><Type>confirmation</Type></Label><Input/></Requirement> <Requirement><Credential><ID>saveCredentials</ID><Type>savecredentials</Type></Credential><Label><Text>onlypassword_remember_my_password</Text><Type>nsg-login-label</Type></Label><Input><CheckBox><InitialValue>false</InitialValue></CheckBox></Input></Requirement> <Requirement><Credential><ID>otpmanage</ID><Type>otpmanage</Type></Credential><Input><Text><Secret>false</Secret><Hidden>true</Hidden><InitialValue>1</InitialValue><Constraint>.+</Constraint></Text></Input></Requirement> <Requirement><Credential><ID>pushregister</ID><Type>nsg_registerpush</Type></Credential><Input><Text><Secret>false</Secret><Hidden>true</Hidden><InitialValue>1</InitialValue><Constraint>.+</Constraint></Text></Input></Requirement> <Requirement><Credential><ID>otpregister</ID><Type>nsg_registerotp</Type></Credential><Input><Text><Secret>false</Secret><Hidden>true</Hidden><InitialValue>1</InitialValue><Constraint>.+</Constraint></Text></Input></Requirement> <Requirement><Credential><ID>loginBtn</ID><Type>none</Type></Credential><Label><Type>none</Type></Label><Input><Button>Logon</Button></Input></Requirement> </Requirements> </AuthenticationRequirements> </AuthenticateResponse>
Using this XML, the nFactor Flow is working without any issues and the /manageotp page shows up without errors in both ways, authenticated via 1FA and 2FA.
Summary
I hope the newly created XML is helping others, stumbling across to publish Manageotp page with integrated 1FA and 2FA to external. I think there are further possibilities, but this was my preferred rock-solid one.