Skip to content

DPoP Mobile App POC

Arnab Dutta edited this page Mar 20, 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-03-20-20_15_07


title WebAuthn Registration Flow for Native App

actor User

User->Android App: submit username, \njans-fido configuration url
Android App-> Android App: save following urls from jans-fido configuration into Mobile database\n1. /attestation/options\n2. /attestation/result\n 3. /assertion/options\n4. /assertion/result

opt registration
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": {...}}
Android App->Android App: generate attestationObject from authenticator using /attestation/options response
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=}

end

opt authentication
Android App-> Android App: 1.show biometric prompt requesting fingerprint\n2. On successful capturing of fingerprint generate asymetric key pair\n and match with key pair stored 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: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