Push Enrollment Flow
Here is the sequence diagram displays the flow of when a user tries to login with push notification or faces with Push MFA.
Flow Steps
1) QRCode contents
QRCode is composed of an uri consisting several parameters. For offline validation, push enrollment also behaves as TOTP authenticator.
Other than TOTP parameters, QRCode uri would contain context_token
and enrollment_url
field.
Here is an example:
otpauth://push/MyApp:[email protected]?issuer=MyApp&secret=SOMESECRET&algorithm=SHA256&digits=6&period=30&context_token=SOME_TOKEN&enrollment_url=https%3A%2F%2Fmytenant.plusauth.com%2Fpush-authenticator%2Fenroll
2) Enrollment Request
After QRCode is scanned, authenticator application should enroll the user to PlusAuth.
For enrollment Authenticator application must satisfy these requirements:
- A JWK KeyPair for validation of context signature
- Have a unique device identifier (Usually generated in first installation)
- Is registered to push service (gcm or apns)
Here is the enrollment request:
curl --request POST \
--url 'https://{{TENANT_ID}}.plusauth.com/push-authenticator/enroll' \
--header 'User-Agent: {{USER_AGENT}}' \
--header 'Authorization: Bearer {{CONTEXT_TOKEN}}' \
--header 'Content-Type: application/json' \
--data '{
"public_key": { },
"private_key": { },
"device_identifier": "{{UNIQUE_DEVICE_ID}}",
"credentials": {
"service": "fcm",
"device_token": "{{FIREBASE_TOKEN}}"
}
}'
3) Push Notification Content
Push Notifications sent by PlusAuth will have an empty title and content but in its payload there will be a context
field.
The context
value is a JWT signed with the private key provided in the enrollment request.
Here is an example of the attributes inside JWT:
null
. For example, in some cases settings.bind_sim
, info.agent
or info.agent.device
can be undefined or null.
Make sure you check the existence of the key before you try to access them{
"exp": 1702250257,
"iat": 1702243057,
"context_token": "CONTEXT_TOKEN",
"resolve_url": "https://mytenant.plusauth.com/push-authenticator/resolve",
"settings": {
"bind_sim": false
},
"info": {
"application": "Application Name",
"ip": "127.0.0.1",
"user_identifier": "[email protected]", // Could be phone number or username or any other user property
"agent": {
"ua": "",
"browser": {
"name": "",
"version": "",
"major": ""
},
"cpu": {
"architecture": ""
},
"device": {
"type": "",
"vendor": "",
"model": ""
},
"engine": {
"name": "",
"version": ""
},
"os": {
"name": "",
"version": ""
}
},
"location": {
"city": "Istanbul", // English city name
"country": "TUR", // Country ISO Code
"postal_code": "34000", // Postal code of location
"coordinates": {
"accuracy_radius": 1,
"latitude": 41.091384,
"longitude": 29.062315,
// The time zone associated with location, as specified by the IANA Time Zone Database
"time_zone": "Europe/Istanbul"
}
}
}
}
{
"type": "code",
"values": [ "83", "21", "12" ]
}
{
"type": "prompt"
}
Settings
If settings object contains bind_sim
value and it is set to true
the authenticator application should prevent receiving
push notification or generating otp code if end-user's SIM card changes.
You can use fields details.is_sim_bound
and details.sim_card_identifiers
per-account in account scheme.
See related api docs from Accounts / Save Account
4) Resolving Push Interaction
After user interacts with push notification (answers prompt or selects code), authenticator application must inform PlusAuth with the interaction result.
To do so, application must create a JWT signed with private key and with corresponding claim containing the action response.
Your JWT must contain following claims depending on the push notification type:
{
// If user rejects interaction
"reject_reason": "ignore", // or "fraud_suspicion"
}
{
"selected_value": "{{SELECTED_VALUE}}",
}
{
"response": "accepted", // or "rejected",
}
Here is the responding request:
curl --request POST \
--url 'https://{{TENANT_ID}}.plusauth.com/push-authenticator/resolve-challenge' \
--header 'User-Agent: {{USER_AGENT}}' \
--header 'Authorization: Bearer {{CONTEXT_TOKEN}}' \
--header 'Content-Type: application/json' \
--data '{
"context": "{{GENERATED_JWT}}",
"device_identifier": "{{UNIQUE_DEVICE_ID}}"
}'