Works on Apereo CAS V 8.0.1.2 - https://github.com/apereo/cas
For older versions of CAS, please check the branch corresponding to your CAS version :
Require esup-otp-api
add the following:
# MFA Esup Otp Authentication
cas.authn.mfa.triggers.global.global-provider-id=mfa-esupotp
# Add translations, you will need to check what are the default from CAS "Message Bundles" properties
cas.messageBundle.baseNames=classpath:custom_messages,classpath:messages,classpath:esupotp_message
# Add your esup-otp-api in Content-Security-Policy:
cas.http-web-request.header.content-security-policy=script-src 'self' 'unsafe-inline' 'unsafe-eval' https://esup-otp-api.univ-ville.fr/; object-src 'none'; worker-src 'self' blob: 'unsafe-inline'
If you want to trust devices for 7 days, you can add this in cas.properties
cas.authn.mfa.trusted.core.device-registration-enabled=true
cas.authn.mfa.trusted.core.auto-assign-device-name=true
cas.authn.mfa.trusted.device-fingerprint.cookie.max-age=P7D
with auto-assign-device-name, user will not have to choose a name for his device in a web form, it will be automatically assigned.
Create esupotp.properties in same directory as cas.properties
##
# Esup Otp Authentication
#
esupotp.rank=0
esupotp.urlApi=http://my-api.com:8081
esupotp.usersSecret=changeit
esupotp.apiPassword=changeit
esupotp.byPassIfNoEsupOtpMethodIsActive=false
esupotp.otpManagerUrl=https://esup-otp-manager.univ.fr/
esupotp.failureMode=CLOSED
In esupotp.properties you can also use usual Multifactor Authentication Bypass configurations described here https://apereo.github.io/cas/8.0.x/mfa/Configuring-Multifactor-Authentication-Bypass.html
So for example you can setup bypass with groovy script :
esupotp.bypass.groovy.location=file:/etc/cas/config/mfaGroovyBypass.groovy
/etc/cas/config/mfaGroovyBypass.groovy :
import java.util.*
def boolean run(authentication, principal, registeredService, provider, logger, httpRequest, ... other_args) {
if(registeredService.id == 10 && "cn=for.appli-sensible.supervisor,ou=groups,dc=univ-ville,dc=fr" in principal.attributes.memberOf) {
return true;
}
return false;
}add
...
dependencies {
...
implementation "org.esup-portail:esup-otp-cas:v2.3.0-cas_v8.0.x"
}add
<AsyncLogger name="org.esupportail.cas.adaptors.esupotp" level="debug" additivity="false" includeLocation="true">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
If you want to use an untagged version, you can use jitpack.io :
Add in cas/build.gradle
...
repositories {
...
maven {
url "https://jitpack.io"
}
}
...
dependencies {
...
implementation "com.github.EsupPortail:esup-otp-cas:master"
}TIPS: Look for https://jitpack.io/#EsupPortail/esup-otp-cas and check the available version you can use
If you want to package locally, with JDK 21 :
./gradlew clean build
This part is only for developers, if you want to publish on central maven repository, you need to have a sonatype account and be a member of the group org.esup-portail.
esup-otp-cas use jrelease plugin to publish on maven central repository.
You have to configure also your ~/.jreleaser/config.yml file with your sonatype credentials.
See https://jreleaser.org/guide/latest/examples/maven/maven-central.html#_gradle
Next, to publish on central maven repository, with JDK 21, and after setting version on gradle.properties :
./gradlew clean build publish jreleaserFullRelease
You can use docker to setup a development environment with CAS and esup-otp-cas.
For that, run
docker compose -f src/etc/docker-compose.yml up
Next, you can access esup-otp-manager on http://localhost:4000/ It will be redirected to CAS for authentication on http://localhost:8080/cas/login?service=http://localhost:4000/
You can use login/password :
- admin/pass
- joe/pass
- jack/pass
Note that some ports on your host must be free :
- 8080 for CAS,
- 3000 for esup-otp-api,
- 4000 for esup-otp-manager
- 3980 for openldap
- 27017 for mongodb
- 5005 for remote debugging of CAS
-> you can debug CAS with remote debugging on port 5005, and you can set breakpoints in esup-otp-cas code to see how it works.
If you want to reset the environment (rebuild esup-otp-api/esup-otp-manager, reset mongodb and rebuild esup-otp-cas with your changes...), you can run
docker compose -f src/etc/docker-compose.yml down -v
docker compose -f src/etc/docker-compose.yml up --build
If you wan to reset only the esup-otp-api database on mongodb (that contains users, otp methods, backup codes...), you can run
docker compose -f src/etc/docker-compose.yml exec mongodb mongosh --eval "db.getSiblingDB('test-otp').dropDatabase()"
You can also run an optional Docker profile that executes a Playwright scenario against the local stack.
The scenario covers:
- access to esup-otp-mamanager on
http://localhost:4000/with the CAS redirect, - login with
joe/pass, - activation of backup codes in
esup-otp-manager, - memorizing one backup code,
- logout of esup-otp-mamanager (and CAS),
- (re)login with
joe/passand access to the MFA challenge, - validation with the saved backup code and successful authentication,
- deactivation of backup codes.
Run it with:
docker compose -f src/etc/docker-compose.yml --profile playwright upThis profile is optional and uses the same host ports as the development stack (4000, 8080, 3000, 3980, 27017).
After the run, inspect the HTML report under src/etc/docker/playwright/playwright-report/; the recorded .webm videos are attached there as report artifacts.
If you want to only rerun the Playwright tests without restarting the whole stack, you can run (--build is useful if you want to rebuild the image with your latest code changes):
docker compose -f src/etc/docker-compose.yml --profile playwright run --rm playwright --buildIt can be useful to reinitialize the esup-otp-api database before running the tests, to ensure a clean state with known users and OTP methods:
docker compose -f src/etc/docker-compose.yml exec mongodb mongosh --eval "db.getSiblingDB('test-otp').dropDatabase()"
