Skip to content

DPoP Mobile App POC

Arnab Dutta edited this page Jun 21, 2024 · 21 revisions

Steps followed in App for DPoP Authentication

1. Fetch OpenID Provider Configuration

a. On the client registration (DCR) screen user enters the OpenID Provider Configuration URL and scope.

b. On click of the Register button the app fetches OpenID Provider configuration using URL and stores it in SQLite database table of android.

2. DCR with attestation

The app extracts registration_endpoint from OpenID Provider configuration and requests for DCR with attestation.

------------
Request
------------
curl -X POST -k -H 'Content-Type: application/json' -i 'https://admin-ui-test.gluu.org/jans-auth/restv1/register' --data '{
"application_type":"web",
"client_name":"DPoPAppClient-bc041d99-2c02-4622-8b6b",
"evidence":"eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IlJTMjU2IiwiandrIjp7ImUiOiJBUUFCIiwia3R5IjoiUlNBIiwibiI6IjNwMmN1aXNjOFJ3U0VYN1dEYUxUamlQRUJ1bVZnTlIzVm96MnBobGh0aDV3Y2R4aGZ3aFpUcFRvOHJUWFBSbWozUWM5ck1jOGo5djM5MWZKbXhmSWpWM2Q0YUJVZm5aOS00VFJzdmlXVEJIN2RkODBtWTZhcnl5OTBaY0ZmTHdNektzSG1ITHJfTENJUjNKZ3F1MG1TTm9EUHFjTmNFcVVPZVZxVTV2YnRXUHJMdVNlMEJTNmxaRDQ2aXZBSWttOUJhb2xqT1NEcGJvV2gtUW5nMzBVQTJhZnhHN05JVjlwV1Y2S2hka0h1dUtDUXN1RlRpa2Z6cGlWQVRaLTlFcllUU1hOMDVqT0RJUk5Mc1FnV0NyUnBiMEdheGlick5OWTZmcWF2dkgycDJGaXlnbEFaUnBqTDhRNkVWd0Noek9uT1BySkpDZXdCdTdHVFZ4dVZuaHc0dyJ9fQ.eyJhYXBOYW1lIjoiRFBvUEFwcCIsImlhdCI6MTY5MzU2OTQ0ODUwMywic2VxIjoiYzlmMWFhOTUtNGZhNi00NGQ4LTljNTYtYzhkNDcwNGJlMTQyIiwianRpIjoiZWRlYWQxOWQtYzQ1Yy00NDVhLWFlN2ItZTM2NjRiOTBhY2ZjIn0.OQhT-lzYIUWRb4YV4tZNHHTOKAZOax8yZW3HONxLNIvUsxTeS7zB55G5M8WmdwVA4UTcRL6oBj_j-Ep_r6X1fx9ZaJ-SmGCONgC8LxHPmWJCb1eQxYq-7S-CWTtS-SCK4FW473Kg-Kk_FSlyy5tFbH5p_x_CTiPuTgWK5Ct9OwbJ9tyaaiqeXoRgBUEjjjElSYoZqutFbe1WmKrlwblVtXc47JzaZBbnDt3Goukz1HUln_xL0sT_CnxaTVLKdAT7IIfK5UuVIEiKvoqathMdKlqynrn9kyLXwflpMWbirWrQxIpIbkQ6srHStyDDh1zBEyYNAj-pXz9iyhHULoz0Ig",
"grant_types":["authorization_code"],
"jwks":"{\"keys\":[{\"e\":\"AQAB\",\"kty\":\"RSA\",\"n\":\"pzVIV71wHi1fG3TEcCUJw0uolOBUryEJPy8IFpI20lAWqWw5prPn2mPqsyaOPpQtNX2_12PmZjlDq3SjInipSK_saSJ4pm-LCTOYZm55n5QbiK-iCR0DgCVlAeNj6YIenk1Tdy0KSuYoZxOL7iOdMAT9f7qvRAlSdWivOef_tDEtCIbZ3aKoNujv0xPUmOgnZ0U1QdxK3bmprV08O6dV6-_GJBeEdGZ5qZezPIxyjaxiGcoDk47QxdFz-aD38md-zQDEr9toU08j08bgeZLvbVr5e_M-fzhGY5yEISg1e-87n_v3HbdKFGuRFqYp3CYVayuYMWJsIIq8aEPZxdi8qw\"}]}",
"redirect_uris":["https://admin-ui-test.gluu.org"],
"response_types":["code"],
"scope":"openid"
}'

------------
Response
------------
{
  "allow_spontaneous_scopes": false,
  "jwks": {
    "keys": [{
      "kty": "RSA",
      "e": "AQAB",
      "n": "pzVIV71wHi1fG3TEcCUJw0uolOBUryEJPy8IFpI20lAWqWw5prPn2mPqsyaOPpQtNX2_12PmZjlDq3SjInipSK_saSJ4pm-LCTOYZm55n5QbiK-iCR0DgCVlAeNj6YIenk1Tdy0KSuYoZxOL7iOdMAT9f7qvRAlSdWivOef_tDEtCIbZ3aKoNujv0xPUmOgnZ0U1QdxK3bmprV08O6dV6-_GJBeEdGZ5qZezPIxyjaxiGcoDk47QxdFz-aD38md-zQDEr9toU08j08bgeZLvbVr5e_M-fzhGY5yEISg1e-87n_v3HbdKFGuRFqYp3CYVayuYMWJsIIq8aEPZxdi8qw"
    }]
  },
  "application_type": "web",
  "rpt_as_jwt": false,
  "registration_client_uri": "https://admin-ui-test.gluu.org/jans-auth/restv1/register?client_id=05fd2650-8478-4bca-9488-b14f7219473b",
  "tls_client_auth_subject_dn": "",
  "run_introspection_script_before_jwt_creation": false,
  "registration_access_token": "872ab5ca-457a-4118-95cf-d699dc549967",
  "client_id": "05fd2650-8478-4bca-9488-b14f7219473b",
  "token_endpoint_auth_method": "client_secret_basic",
  "scope": "openid",
  "client_secret": "97c734ca-a729-49b7-9ec3-514e43ba530b",
  "client_id_issued_at": 1693571537,
  "backchannel_logout_uri": [],
  "backchannel_logout_session_required": false,
  "client_name": "DPoPAppClient-bc041d99-2c02-4622-8b6b",
  "par_lifetime": 600,
  "spontaneous_scopes": [],
  "id_token_signed_response_alg": "RS256",
  "access_token_as_jwt": false,
  "grant_types": ["authorization_code"],
  "subject_type": "pairwise",
  "additional_token_endpoint_auth_methods": [],
  "keep_client_authorization_after_expiration": false,
  "require_par": false,
  "redirect_uris": ["https://admin-ui-test.gluu.org"],
  "redirect_uris_regex": "",
  "additional_audience": [],
  "frontchannel_logout_session_required": false,
  "client_secret_expires_at": 0,
  "access_token_signing_alg": "RS256",
  "response_types": ["code"]
}

3. Execute Authorization Challenge Endpoint to get the Authorization Code

a. Once DCR is successful the app save OIDC client details in SqlLite DB. Every time the user opens the app, it check if the client details are present in DB. If yes, then it directly forwards to Login page asking for username/password.

b. On submitting username/password the app extracts authorization_challenge_endpoint from the OpenID Provider configuration and requests to get the Authorization Code.

------------
Request
------------

curl -k https://admin-ui-test.gluu.org/jans-auth/restv1/authorization_challenge -d 'grant_type=authorization_code' -d 'scope=openid' -d 'username=admin' -d 'password=secret' -d 'state=0x74ab847000' -d 'nonce=8974ab847000' -d 'client_id=05fd2650-8478-4bca-9488-b14f7219473b'

------------
Response
------------
{"authorization_code":"cdf09c4a-10f4-467e-9f6f-a697db44b108"}

4. DPop Token using Authorization Code.

The app extracts token_endpoint from the OpenID Provider configuration and requests to get the access_token.

------------
Request
------------

curl -k -u '05fd2650-8478-4bca-9488-b14f7219473b:97c734ca-a729-49b7-9ec3-514e43ba530b' -H 'DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IlJTMjU2IiwiandrIjp7ImUiOiJBUUFCIiwia3R5IjoiUlNBIiwibiI6IjNwMmN1aXNjOFJ3U0VYN1dEYUxUamlQRUJ1bVZnTlIzVm96MnBobGh0aDV3Y2R4aGZ3aFpUcFRvOHJUWFBSbWozUWM5ck1jOGo5djM5MWZKbXhmSWpWM2Q0YUJVZm5aOS00VFJzdmlXVEJIN2RkODBtWTZhcnl5OTBaY0ZmTHdNektzSG1ITHJfTENJUjNKZ3F1MG1TTm9EUHFjTmNFcVVPZVZxVTV2YnRXUHJMdVNlMEJTNmxaRDQ2aXZBSWttOUJhb2xqT1NEcGJvV2gtUW5nMzBVQTJhZnhHN05JVjlwV1Y2S2hka0h1dUtDUXN1RlRpa2Z6cGlWQVRaLTlFcllUU1hOMDVqT0RJUk5Mc1FnV0NyUnBiMEdheGlick5OWTZmcWF2dkgycDJGaXlnbEFaUnBqTDhRNkVWd0Noek9uT1BySkpDZXdCdTdHVFZ4dVZuaHc0dyJ9fQ.eyJodG0iOiJQT1NUIiwiaHR1IjoiaHR0cHM6Ly9hZG1pbi11aS10ZXN0LmdsdXUub3JnIiwiaWF0IjoxNjkzNTY5NDg0NTQ0LCJqdGkiOiI1MWY0ZWRkYy00MzZjLTRjMmQtYmU1OS1mZjkzNTNiNDI0OTQifQ.MLCDkwyaWVV5kiowWJdnvT241zL0UwMrZ5Kes-uQ_12tKROC0BSZkf2PQ08PcA9nGEw5Ab4MPJOOQlLZAXqkiI7W2zx9BjLIWnRkkSkCS3ucRIfJWxvuQ4W1bfbtUvy9UTZ3UZSD4pxdsZSoGRADePH6JEJy4GkWH6qMrCHeLr2YEjeaBPzJ3Z4YazX8p7kt7_dbNz56hU78hiqu9JetM0-9_5CLqxbomsc8KzreWZ-Mazt_iJJHp_pEKZouClbieKsI_lWfko7MyzxIZNJm6FKdMtCi8SWsXRnuLUkNES0TBe61-yJjqRFqJGZ9BOmwIo8txkmuFHqd403ibaPiiw' https://admin-ui-test.gluu.org/jans-auth/restv1/token -d 'grant_type=authorization_code' -d 'scope=openid' -d 'code=c13327d9-9a1c-41f7-8d3c-06d83ee7f69f' -d 'redirect_uri=https://admin-ui-test.gluu.org/callback'

------------
Response
------------

{"access_token":"fb49e7e7-2831-4dfd-8947-30ea2c6f305e","scope":"openid","id_token":"eyJraWQiOiJjb25uZWN0X2E4MWQyZWIzLTY2YTYtNGEzOS1hN2I2LTEzODBiMzAzYTYwNl9zaWdfcnMyNTYiLCJ0eXAiOiJqd3QiLCJhbGciOiJSUzI1NiJ9.eyJhdF9oYXNoIjoiTG5ocnFjR0ZiYmw1X3ZCaG1Yanc1ZyIsInN1YiI6IkxVS0RHR0Q3aXQtb3lqUFE3akV6RV90WF9IZVVvcVRjeU1weTNNZ0ZIa0EiLCJhbXIiOltdLCJpc3MiOiJodHRwczovL2FkbWluLXVpLXRlc3QuZ2x1dS5vcmciLCJub25jZSI6Ijg5NzRhYjg0NzAwMCIsImphbnNPcGVuSURDb25uZWN0VmVyc2lvbiI6Im9wZW5pZGNvbm5lY3QtMS4wIiwiYXVkIjoiMDVmZDI2NTAtODQ3OC00YmNhLTk0ODgtYjE0ZjcyMTk0NzNiIiwicmFuZG9tIjoiMDg4ZmQ5MTUtNDA5NC00NDgxLTlkODEtZTllZDdhMTY3MGE3IiwiYWNyIjoiZGVmYXVsdF9jaGFsbGVuZ2UiLCJjX2hhc2giOiJjZmJ0d0ZWek51QzhpVDFFQWtJdGlnIiwiYXV0aF90aW1lIjoxNjkzNTc0Mzg0LCJleHAiOjE2OTM1NzgwMTgsImdyYW50IjoiYXV0aG9yaXphdGlvbl9jb2RlIiwiaWF0IjoxNjkzNTc0NDE4fQ.ODhYZTdC0IJjidUAMyVbRKKoovyW1-2AdzVPtn6tx232Vjx6Mxs-Sng57YV6lUdjFLbYfmUjn5JM90Zg3buJHpu6gZKaLs_44j-1i9NbJrAFhe_6libXO0Bib4v5vacYx_0zllXO5FklDdvctahxFc9-xG8pTolkqEGJGjILyNI70NOjodZxBTPPZDBSER9MnD0V_CmRM0izonroV_HyvWDCbTJm9sjBufoY2UkpKCYpyKytu5zSUFq6gq3DEX661Vx_ANgpzy_0BkJgfQ4-juvVPoBdcQnqK7akpwgqyf82NK-pFSZXdduO1Z343L_uYyzzurEkMHf6s_8jre4EXw","token_type":"DPoP","expires_in":299}

5. Get User Info

The app extracts userinfo_endpoint from the OpenID Provider configuration and requests to user-info.

------------
Request
------------

curl -k  -H 'Authorization=Bearer fb49e7e7-2831-4dfd-8947-30ea2c6f305e' https://admin-ui-test.gluu.org/jans-auth/restv1/userinfo -d 'access_token=fb49e7e7-2831-4dfd-8947-30ea2c6f305e'

------------
Response
------------

{"sub":"LUKDGGD7it-oyjPQ7jEzE_tX_HeUoqTcyMpy3MgFHkA"}

6. Logout

The access token is revoked in the logout process.

**References: ** https://github.com/JanssenProject/jans/wiki/Mobile-DPoP-FIDO-Authn

FIDO Registration and Authentication (using jans-fido)

  1. Using BiometricPrompt which provides a consistent user interface to fingerprint registration/authentication.

  2. Using Authenticator : https://github.com/duo-labs/android-webauthn-authenticator/tree/master

Reference:

https://developers.yubico.com/WebAuthn/WebAuthn_Developer_Guide/WebAuthn_Client_Registration.html

https://webauthn.guide/#registration

screencapture-sequencediagram-org-2024-06-21-22_40_23


title WebAuthn Registration/ Authentication Flow for Native App

actor User

User->Android App: Open App
Android App->Android App: retrive the issuer from SSA 
Android App<->jans-auth-server: app fetches OP configuration and save it mobile database
Android App<->jans-fido: app fetches FIDO configuration and save it in mobile database
Android App->jans-auth-server: registers OpenId client using SSA
Android App->User: shows screen for Passkey Sign-In and Sign-Up

opt registration
User->Android App: submit username / password
Android App<->jans-auth-server: OAuth2 DPop authentication to fetch user-info
Android App-> Android App: 1.show biometric prompt requesting fingerprint\n2. On successful enrolment generate asymetric key pair in android keystore
Android App->jans-fido: POST /attestation/options \n { "username": "admin",\n "displayName": "admin",\n  "attestation": "none"}
jans-fido->Android App: PublicKeyCredentialCreationOptions:\n {"attestation": "none",\n "authenticatorSelection": {},\n "challenge": "vr27LUiJZF2CN4AncVTsAWlJzAVNsofcr8zM3Ej2Dk8",\n "pubKeyCredParams": [...],\n "rp": {...},\n "user": {...},\n "clientDataHash": "..."}
Android App->Android App: signs concatenation of authenticatorData and clientDataHash from /attestation/options response to create AttestationObject. \n The private key generated on bimetric enrolment is used for signing.

Android App->Android App: generate ClientDataJSON and ClientDataHash using challenge, type: webauthn.create and origin (jans-fido host)
Android App->jans-fido: POST /attestation/result \n {"id": "bas64 encoded attestationObject.getCredentialId() ...",\n"type": "public-key",\n "response": {\n"attestationObject": "base64url encoded attestationObject",\n       "clientDataJSON": "base64url encoded clientDataJSON"\n }}
jans-fido->Android App: {createdCredentials={type=public-key, id=BS7CHyycngx9H3D8oZQ2sohKBsEDkQ_yoJTClWwR95Y=}, \nstatus=ok, errorMessage=}
Android App->User: Show after login page with user-info

end

opt authentication
User->Android App: select the passkey to sign in
Android App-> Android App: 1.show biometric prompt requesting fingerprint\n2. On successful matching of fingerprint select the asymetric key pair stored for same passkey during enrolment
Android App->jans-fido: POST /assertion/options\n{"username": "admin"}
jans-fido->Android App:AssertionOptionResponse\n{challenge='XCijJZZqvUPHf-SEuMID5Ef9lP5jcsG3A6blPtGy7e0',\n user='null',\n userVerification='preferred', \nrpId='admin-ui-test.gluu.org', \nstatus='ok',\n errorMessage='', \nallowCredentials=[...]}
Android App->Android App: Construct authenticatorData and sign it using private key
Android App-> Android App:generate ClientDataJSON and ClientDataHash using challenge, type: webauthn.get and origin (jans-fido host)
Android App-> Android App: get assertionObject from authenticator using above response and clientDataHash
Android App->jans-fido: POST /assertion/result\n{"id": "base64 encoded assertionObject.CredentialId",\n "type":"public-key",\n "rawId": "base64 encoded assertionObject.CredentialId",\n "response":{"clientDataJSON": "bas64 encoded ClientDataJSON", \n"authenticatorData": "base64 encoded assertionObject.authenticatorData",\n "signature": "base64 encoded assertionObject.signature"\n}}
jans-fido->Android App: {authenticatedCredentials={type=public-key, id=UIIoHYAJygJgfBJeSloipjLT34AHznSAuDZyj75aPMs=}, status=ok, errorMessage=, username=admin}
end

Clone this wiki locally