Table of Contents
References
repository: https://github.com/vacuumlabs/safi-pki-auth-poc
PoC description: Project Portunus (IAM PKI)
PoC’s goal is to prove feasibility of IAM for Native Apps
Test site: https://auth-1.sandbox.safibank.online
High Level System Architecture
App Architecture
Generic Data Flow
This is the data flow for both registration and assertion (and message sign):
Frontend API
iOS
Start Request
Start request via ASAuthorizationPlatformPublicKeyCredentialProvider
Create the object with a relying party identifier:init(relyingPartyIdentifier: String)
Create a registration request with a challenge, name, and user ID:func createCredentialRegistrationRequest(challenge: Data, name: String, userID: Data)
-> ASAuthorizationPlatformPublicKeyCredentialRegistrationRequest
Note that challenge
is binary and we may need to convert it from Base64URL encoding when received from the Auth Svr.
Registration Request
ASAuthorizationPlatformPublicKeyCredentialRegistrationRequest
implements ASAuthorizationPublicKeyCredentialRegistrationRequest which has
var attestationPreference: ASAuthorizationPublicKeyCredentialAttestationKind
- The type of attestation you're requesting.
static let none: ASAuthorizationPublicKeyCredentialAttestationKind
- An attestation kind of none.
static let direct: ASAuthorizationPublicKeyCredentialAttestationKind
- An attestation kind of direct.
static let enterprise: ASAuthorizationPublicKeyCredentialAttestationKind
- An attestation kind of enterprise.
static let indirect: ASAuthorizationPublicKeyCredentialAttestationKind
- An attestation kind of indirect.
var challenge: Data
- Arbitrary data that the client signs as proof of a valid registration or attestation.
var displayName: String?
- A user-visible name for the credential, such as the account's user name.
var name: String
- A user-visible name that identifies a credential.
var relyingPartyIdentifier: String
- The domain name of the website for the credential.
var userID: Data
- Data that the relying party associates with the credential.
var userVerificationPreference: ASAuthorizationPublicKeyCredentialUserVerificationPreference
- A preference for whether the authenticator attempts to verify the user at the time of registration.
static let discouraged: ASAuthorizationPublicKeyCredentialUserVerificationPreference
- The relying party discourages user verification.
static let preferred: ASAuthorizationPublicKeyCredentialUserVerificationPreference
- The relying party prefers user verification.
static let required: ASAuthorizationPublicKeyCredentialUserVerificationPreference
- The relying party requires user verification.
Sign-in (Assertion) Request
Create an assertion request with a challenge:func createCredentialAssertionRequest(challenge: Data)
-> ASAuthorizationPlatformPublicKeyCredentialAssertionRequest
Note that challenge
is binary and we may need to convert it from Base64URL encoding when received from the Auth Svr.
ASAuthorizationPlatformPublicKeyCredentialAssertionRequest implements
ASAuthorizationPublicKeyCredentialAssertionRequest which has
var challenge: Data
- The challenge to sign.
var relyingPartyIdentifier: String
- The domain name of the website for the credential.
var allowedCredentials: [ASAuthorizationPublicKeyCredentialDescriptor]
- A list of allowed credential descriptors the user attempts to sign in with.
var credentialID: Data
- An identifier the authenticator generates during registration to uniquely identify a specific credential.
var userVerificationPreference: ASAuthorizationPublicKeyCredentialUserVerificationPreference
- A preference that indicates whether the authenticator attempts to verify the user at the time of sign-in.
static let discouraged: ASAuthorizationPublicKeyCredentialUserVerificationPreference
- The relying party discourages user verification.
static let preferred: ASAuthorizationPublicKeyCredentialUserVerificationPreference
- The relying party prefers user verification.
static let required: ASAuthorizationPublicKeyCredentialUserVerificationPreference
- The relying party requires user verification.
Process Reply
Reply is received via implementing some functions in ASAuthorizationControllerDelegate
Tells the delegate when authorization completes successfully:func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization: ASAuthorization)
The parameter didCompleteWithAuthorization.credential
can be ASAuthorizationPlatformPublicKeyCredentialRegistration or ASAuthorizationPlatformPublicKeyCredentialAssertion
Registration Reply
ASAuthorizationPlatformPublicKeyCredentialRegistration
implements ASAuthorizationPublicKeyCredentialRegistration which has
var rawAttestationObject: Data?
- a WebAuthn attestation object (in CBOR format). Note that this is binary and may need to be converted to Base64URL to be transmitted to the Auth Srvr.
(reference: https://cbor.io/spec.html)
ASAuthorizationPublicKeyCredentialRegistration
implements ASPublicKeyCredential which has
var rawClientDataJSON: Data
- Raw data that contains a JSON-compatible encoding of the client data.var credentialID: Data
- An identifier that the authenticator generated during registration to uniquely identify a specific credential.
Sign-in (Assertion) Reply
ASAuthorizationPlatformPublicKeyCredentialAssertion
implements ASAuthorizationPublicKeyCredentialAssertion which has
var signature: Data!
- The signature for the assertionvar userID: Data!
- A user identifier for the assertion.var rawAuthenticatorData: Data!
- A byte sequence that contains additional information about the credential.
Error Handling
Tells the delegate when authorization fails, and provides an error explaining why:func authorizationController(controller: ASAuthorizationController, didCompleteWithError: Error)
Change or reset a key
Use the same createCredentialRegistrationRequest
as above. Registering a passkey with the same userID
as an existing one overwrites the existing passkey on the user's devices.
Backend API - auth-service
Deployed at https://auth-1.sandbox.safibank.online/
GET /api/v1
info about available endpoints, in JSON format
GET /api/v1/version
info about deployed code version, etc.
POST /api/v1/register
initial call to be issued when registering a new username
request:
add non-resident credential
{ "username":"Norbi6", "displayName":"Norbi6", "credentialNickname":"", "sessionToken":null }
add resident credential
{ "username":"norbi22", "displayName":"norbi22", "credentialNickname":"", "requireResidentKey":true, "sessionToken":null }
response:
success
non-resident credential: returns credential ID, challenge string and continuation action URL (among others)
{ "success":true, "request":{ "username":"Norbi6", "credentialNickname":"", "requestId":"WpaCANAUFJ42JdcZgDsUqr4pmUCD6G0cYFjmKfJR_eg", "publicKeyCredentialCreationOptions":{ "rp":{ "name":"auth PoC", "id":"safibank.online" }, "user":{ "name":"Norbi6", "displayName":"Norbi6", "id":"HVb6FEbU4haJAqJALbf0vboSmqKiPbZqFmXtpuvISCw" }, "challenge":"nUGbR2MALTnEVmoK4BgVOOgODVbldpnhc9hME4TSEfI", "pubKeyCredParams":[ { "alg":-7, "type":"public-key" }, { "alg":-257, "type":"public-key" } ], "excludeCredentials":[ ], "authenticatorSelection":{ "residentKey":"discouraged" }, "attestation":"direct", "extensions":{ "appidExclude":"https://auth-1.sandbox.safibank.online:8443", "credProps":true } }, "sessionToken":"Rng4gnX6Teduc_-4qVK_UEAizoXsIfKlZxznUhz5VrE" }, "actions":{ "finish":"https://auth-1.sandbox.safibank.online/api/v1/register/finish" } }
resident credential: returns credential ID, challenge string and continuation action URL (among others)
Note: the only semantic difference compared to the previous one is the value ofauthenticatorSelection.residentKey
isrequired
{ "success":true, "request":{ "username":"norbi22", "credentialNickname":"", "requestId":"2DnHO2YZdF06Lud2gYgBND09u8ok5yPV3mNHn70bZCY", "publicKeyCredentialCreationOptions":{ "rp":{ "name":"auth PoC", "id":"auth-1.sandbox.safibank.online" }, "user":{ "name":"norbi22", "displayName":"norbi22", "id":"cDNuvwcp5pkgfwU0y5kK_-yJutLtK5t2PJP_8Ct5vkw" }, "challenge":"cb7PqQqJrJmJAhqVtIsqkXLVJWhnWBT8iZ0kvg10mKs", "pubKeyCredParams":[ { "alg":-7, "type":"public-key" }, { "alg":-257, "type":"public-key" } ], "excludeCredentials":[ ], "authenticatorSelection":{ "residentKey":"required" }, "attestation":"direct", "extensions":{ "appidExclude":"https://auth-1.sandbox.safibank.online:8443", "credProps":true } }, "sessionToken":"czKWyCQxQ9AvWOtjzj3k3B9LIqxMIpYfTSsvTsdc6MU" }, "actions":{ "finish":"https://auth-1.sandbox.safibank.online/api/v1/register/finish" } }
returns error if username is already in use
{"messages":["The username \"Norbi6\" is already registered."]}
POST /api/v1/register/finish
request: to be issued after the client received success response from POST /api/v1/register and after client generated keypair and payload will contain public key to be stored by server
non-resident credential:
{ "requestId":"WpaCANAUFJ42JdcZgDsUqr4pmUCD6G0cYFjmKfJR_eg", "credential":{ "type":"public-key", "id":"lWDLU2GjHm4D6-MNys7lNpKuBMPSNGY_UDSEwhTHdUbwZX0jzHN0Fqr_6uj6UC_yd9ZCxfgcf5WooqtxNXYtIQta4zhRoxwADEHR9zHO", "rawId":"lWDLU2GjHm4D6-MNys7lNpKuBMPSNGY_UDSEwhTHdUbwZX0jzHN0Fqr_6uj6UC_yd9ZCxfgcf5WooqtxNXYtIQta4zhRoxwADEHR9zHO", "response":{ "clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiblVHYlIyTUFMVG5FVm1vSzRCZ1ZPT2dPRFZibGRwbmhjOWhNRTRUU0VmSSIsIm9yaWdpbiI6Imh0dHBzOi8vYXV0aC0xLnNhbmRib3guc2FmaWJhbmsub25saW5lIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ", "attestationObject":"o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZyZjc2lnWEYwRAIgRKMU3Sxg46Mx1uXymFZnfikCzzb4ksgdnFjjX-Z3ARICIAZh_0sUm6dYYHTiOwhRbTJMJzo_d7fXcKyiEaJieB5oaGF1dGhEYXRhWNJe3ul-V0R7-KBuTV13Q9hjEiWmASzJaXm2INyHcopXSkUAAAAArc4AAjW8xgpkiwsl8fBVAwBOlWDLU2GjHm4D6-MNys7lNpKuBMPSNGY_UDSEwhTHdUbwZX0jzHN0Fqr_6uj6UC_yd9ZCxfgcf5WooqtxNXYtIQta4zhRoxwADEHR9zHOpQECAyYgASFYILZ0Vw60L-ahKs-HuhAw1mGTO1SQuncnIkzSfJlLF4x9Ilgg_wLEEQj1VROa-v3ITWURwjgK8aePzF2KVEq4WDV0mGM", "transports":[ "internal" ] }, "clientExtensionResults":{ "credProps":{ "rk":false } } }, "sessionToken":"Rng4gnX6Teduc_-4qVK_UEAizoXsIfKlZxznUhz5VrE" }
resident credential (the only difference compared to the previous one is the value of
clientExtensionResults.credProps.rk
){ "requestId":"2DnHO2YZdF06Lud2gYgBND09u8ok5yPV3mNHn70bZCY", "credential":{ "type":"public-key", "id":"fkB33Lg5CGJutWwI2sCx4zuOkc3k69FUs4XDPOibpa7U3CTPyL55VbJm5gm0lCFpd4Ltz8Gp-7zFIJr6vV3K6PxLc0PchHFhXjGOeME-G44", "rawId":"fkB33Lg5CGJutWwI2sCx4zuOkc3k69FUs4XDPOibpa7U3CTPyL55VbJm5gm0lCFpd4Ltz8Gp-7zFIJr6vV3K6PxLc0PchHFhXjGOeME-G44", "response":{ "clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiY2I3UHFRcUpySm1KQWhxVnRJc3FrWExWSldobldCVDhpWjBrdmcxMG1LcyIsIm9yaWdpbiI6Imh0dHBzOi8vYXV0aC0xLnNhbmRib3guc2FmaWJhbmsub25saW5lIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ", "attestationObject":"o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZyZjc2lnWEcwRQIgMI7G84xGaF_S3ZnW3W7B2pfU85MyAwYuDh0YJf_IysICIQCFIxIL7tDAnc6_gMPqa0vcFj86wfATbTYcW4IzskP8bmhhdXRoRGF0YVjURxYUfbcZhfKoSTmiUwejao264CviCUGv2c1ik6XbL9JFAAAAAK3OAAI1vMYKZIsLJfHwVQMAUH5Ad9y4OQhibrVsCNrAseM7jpHN5OvRVLOFwzzom6Wu1Nwkz8i-eVWyZuYJtJQhaXeC7c_Bqfu8xSCa-r1dyuj8S3ND3IRxYV4xjnjBPhuOpQECAyYgASFYIExNEiVXRgcghsYkHfWX7V7MmdX6BMzSWJwXHuZdSKnmIlggiHvLLjyBkpEyHlMbPaG7qqPQJfibtyeQFq5kP_nESlk", "transports":[ "internal" ] }, "clientExtensionResults":{ "credProps":{ "rk":true } } }, "sessionToken":"czKWyCQxQ9AvWOtjzj3k3B9LIqxMIpYfTSsvTsdc6MU" }
response: all the details about the registration procedure (most probably just to display it on the web UI)
non-resident credential:
{ "success":true, "request":{ // very same structure that is in the response // for the POST /api/v1/register call }, "response":{ // very same structure that is in the request // for the POST /api/v1/register/finish call }, "registration":{ "userIdentity":{ "name":"Norbi6", "displayName":"Norbi6", "id":"HVb6FEbU4haJAqJALbf0vboSmqKiPbZqFmXtpuvISCw" }, "credentialNickname":"", "transports":[ "internal" ], "credential":{ "credentialId":"lWDLU2GjHm4D6-MNys7lNpKuBMPSNGY_UDSEwhTHdUbwZX0jzHN0Fqr_6uj6UC_yd9ZCxfgcf5WooqtxNXYtIQta4zhRoxwADEHR9zHO", "userHandle":"HVb6FEbU4haJAqJALbf0vboSmqKiPbZqFmXtpuvISCw", "publicKeyCose":"pQECAyYgASFYILZ0Vw60L-ahKs-HuhAw1mGTO1SQuncnIkzSfJlLF4x9Ilgg_wLEEQj1VROa-v3ITWURwjgK8aePzF2KVEq4WDV0mGM", "signatureCount":0 }, "username":"Norbi6", "registrationTime":"2022-07-01T17:58:10.554237Z" }, "attestationTrusted":false, "authData":{ "rpIdHash":"5edee97e57447bf8a06e4d5d7743d8631225a6012cc96979b620dc87728a574a", "flags":{ "value":69, "UP":true, "UV":true, "AT":true, "ED":false }, "signatureCounter":0, "attestedCredentialData":{ "aaguid":"adce000235bcc60a648b0b25f1f05503", "credentialId":"9560cb5361a31e6e03ebe30dcacee53692ae04c3d234663f503484c214c77546f0657d23cc737416aaffeae8fa502ff277d642c5f81c7f95a8a2ab7135762d210b5ae33851a31c000c41d1f731ce", "publicKey":"a5010203262001215820b674570eb42fe6a12acf87ba1030d661933b5490ba7727224cd27c994b178c7d225820ff02c41108f555139afafdc84d6511c2380af1a78fcc5d8a544ab85835749863" }, "extensions":null }, "username":"Norbi6", "sessionToken":"Rng4gnX6Teduc_-4qVK_UEAizoXsIfKlZxznUhz5VrE" }
resident credential:
{ "success":true, "request":{ // very same structure that is in the response // for the POST /api/v1/register call }, "response":{ // very same structure that is in the request // for the POST /api/v1/register/finish call }, "registration":{ "userIdentity":{ "name":"norbi22", "displayName":"norbi22", "id":"cDNuvwcp5pkgfwU0y5kK_-yJutLtK5t2PJP_8Ct5vkw" }, "credentialNickname":"", "transports":[ "internal" ], "credential":{ "credentialId":"fkB33Lg5CGJutWwI2sCx4zuOkc3k69FUs4XDPOibpa7U3CTPyL55VbJm5gm0lCFpd4Ltz8Gp-7zFIJr6vV3K6PxLc0PchHFhXjGOeME-G44", "userHandle":"cDNuvwcp5pkgfwU0y5kK_-yJutLtK5t2PJP_8Ct5vkw", "publicKeyCose":"pQECAyYgASFYIExNEiVXRgcghsYkHfWX7V7MmdX6BMzSWJwXHuZdSKnmIlggiHvLLjyBkpEyHlMbPaG7qqPQJfibtyeQFq5kP_nESlk", "signatureCount":0 }, "username":"norbi22", "registrationTime":"2022-07-07T17:31:39.151539Z" }, "attestationTrusted":false, "authData":{ "rpIdHash":"4716147db71985f2a84939a25307a36a8dbae02be20941afd9cd6293a5db2fd2", "flags":{ "value":69, "UP":true, "UV":true, "AT":true, "ED":false }, "signatureCounter":0, "attestedCredentialData":{ "aaguid":"adce000235bcc60a648b0b25f1f05503", "credentialId":"7e4077dcb83908626eb56c08dac0b1e33b8e91cde4ebd154b385c33ce89ba5aed4dc24cfc8be7955b266e609b49421697782edcfc1a9fbbcc5209afabd5dcae8fc4b7343dc8471615e318e78c13e1b8e", "publicKey":"a50102032620012158204c4d12255746072086c6241df597ed5ecc99d5fa04ccd2589c171ee65d48a9e6225820887bcb2e3c819291321e531b3da1bbaaa3d025f89bb7279016ae643ff9c44a59" }, "extensions":null }, "username":"norbi22", "sessionToken":"czKWyCQxQ9AvWOtjzj3k3B9LIqxMIpYfTSsvTsdc6MU" }
POST /api/v1/authenticate
initial call to be issued when authenticating the user
request
non-resident credential:
{ "username" : "Norbi6" }
resident credential:
{}
response: returns success if username is already registered on server and contains challenge to be encrypted by client with private key
non-resident credential:
{ "success":true, "request":{ "requestId":"RhcCrGu5ZJyoC5UlAStvoFyyvwIUtbc9LJAtmE3blhk", "publicKeyCredentialRequestOptions":{ "challenge":"3435vQ1fxdciVGRCpB4yY9JzB9YK8WrbM702FSLHWVs", "rpId":"safibank.online", "allowCredentials":[ { "type":"public-key", "id":"lWDLU2GjHm4D6-MNys7lNpKuBMPSNGY_UDSEwhTHdUbwZX0jzHN0Fqr_6uj6UC_yd9ZCxfgcf5WooqtxNXYtIQta4zhRoxwADEHR9zHO", "transports":[ "internal" ] } ], "extensions":{ "appid":"https://auth-1.sandbox.safibank.online:8443" } }, "username":"Norbi6" }, "actions":{ "finish":"https://auth-1.sandbox.safibank.online/api/v1/authenticate/finish" } }
resident credential:
{ "success":true, "request":{ "requestId":"jOBtVwb2qeNBXXxZqJw87EB2rnpZuiDnfiuO48vSbmU", "publicKeyCredentialRequestOptions":{ "challenge":"kW9eHrQkJboPyO7sNO0GVUV_sgrsokQJIItUlZ0YeqY", "rpId":"auth-1.sandbox.safibank.online", "extensions":{ "appid":"https://auth-1.sandbox.safibank.online:8443" } } }, "actions":{ "finish":"https://auth-1.sandbox.safibank.online/api/v1/authenticate/finish" } }
POST /api/v1/authenticate/finish
to be issued after the client received success response from POST /api/v1/authenticate and after client encrypted the challenge with the private key
request: payload will contain encrypted challenge, to be decrypted on server with the stored public key corresponding to that user
non-resident credential:
{ "requestId":"RhcCrGu5ZJyoC5UlAStvoFyyvwIUtbc9LJAtmE3blhk", "credential":{ "type":"public-key", "id":"lWDLU2GjHm4D6-MNys7lNpKuBMPSNGY_UDSEwhTHdUbwZX0jzHN0Fqr_6uj6UC_yd9ZCxfgcf5WooqtxNXYtIQta4zhRoxwADEHR9zHO", "rawId":"lWDLU2GjHm4D6-MNys7lNpKuBMPSNGY_UDSEwhTHdUbwZX0jzHN0Fqr_6uj6UC_yd9ZCxfgcf5WooqtxNXYtIQta4zhRoxwADEHR9zHO", "response":{ "clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiMzQzNXZRMWZ4ZGNpVkdSQ3BCNHlZOUp6QjlZSzhXcmJNNzAyRlNMSFdWcyIsIm9yaWdpbiI6Imh0dHBzOi8vYXV0aC0xLnNhbmRib3guc2FmaWJhbmsub25saW5lIiwiY3Jvc3NPcmlnaW4iOmZhbHNlLCJvdGhlcl9rZXlzX2Nhbl9iZV9hZGRlZF9oZXJlIjoiZG8gbm90IGNvbXBhcmUgY2xpZW50RGF0YUpTT04gYWdhaW5zdCBhIHRlbXBsYXRlLiBTZWUgaHR0cHM6Ly9nb28uZ2wveWFiUGV4In0", "authenticatorData":"Xt7pfldEe_igbk1dd0PYYxIlpgEsyWl5tiDch3KKV0oFAAAAAA", "signature":"MEYCIQDMzAjUmVyvK4ubc2fPqno_y0hFfTI4kFbQd9_47RQZjAIhAKLMJWm8DE3buizE-llz-QIuaUMcmBIwD69kf0XYTbj7", "userHandle":"HVb6FEbU4haJAqJALbf0vboSmqKiPbZqFmXtpuvISCw" }, "clientExtensionResults":{ "appid":false } }, "sessionToken":"Rng4gnX6Teduc_-4qVK_UEAizoXsIfKlZxznUhz5VrE" }
non-resident credential:
{ "requestId":"jOBtVwb2qeNBXXxZqJw87EB2rnpZuiDnfiuO48vSbmU", "credential":{ "type":"public-key", "id":"fkB33Lg5CGJutWwI2sCx4zuOkc3k69FUs4XDPOibpa7U3CTPyL55VbJm5gm0lCFpd4Ltz8Gp-7zFIJr6vV3K6PxLc0PchHFhXjGOeME-G44", "rawId":"fkB33Lg5CGJutWwI2sCx4zuOkc3k69FUs4XDPOibpa7U3CTPyL55VbJm5gm0lCFpd4Ltz8Gp-7zFIJr6vV3K6PxLc0PchHFhXjGOeME-G44", "response":{ "clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoia1c5ZUhyUWtKYm9QeU83c05PMEdWVVZfc2dyc29rUUpJSXRVbFowWWVxWSIsIm9yaWdpbiI6Imh0dHBzOi8vYXV0aC0xLnNhbmRib3guc2FmaWJhbmsub25saW5lIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ", "authenticatorData":"RxYUfbcZhfKoSTmiUwejao264CviCUGv2c1ik6XbL9IFAAAAAA", "signature":"MEUCIEKX-ai9OE5gnH5hHQT3ndC9B7kPU4n12Scj2tni4zQYAiEAokQZwfHNvXGVw1Ems0iIDW6rxMs9LVrXgtBPc8PL8cc", "userHandle":"cDNuvwcp5pkgfwU0y5kK_-yJutLtK5t2PJP_8Ct5vkw" }, "clientExtensionResults":{ "appid":false } }, "sessionToken":null }
response:
non-resident credential:
{ "success":true, "request":{ // very same structure that is in the response // for the POST /api/v1/authenticate call }, "response":{ // very same structure that is in the request // for the POST /api/v1/authenticate/finish call }, "registrations":[ { "userIdentity":{ "name":"Norbi6", "displayName":"Norbi6", "id":"HVb6FEbU4haJAqJALbf0vboSmqKiPbZqFmXtpuvISCw" }, "credentialNickname":"", "transports":[ "internal" ], "credential":{ "credentialId":"lWDLU2GjHm4D6-MNys7lNpKuBMPSNGY_UDSEwhTHdUbwZX0jzHN0Fqr_6uj6UC_yd9ZCxfgcf5WooqtxNXYtIQta4zhRoxwADEHR9zHO", "userHandle":"HVb6FEbU4haJAqJALbf0vboSmqKiPbZqFmXtpuvISCw", "publicKeyCose":"pQECAyYgASFYILZ0Vw60L-ahKs-HuhAw1mGTO1SQuncnIkzSfJlLF4x9Ilgg_wLEEQj1VROa-v3ITWURwjgK8aePzF2KVEq4WDV0mGM", "signatureCount":0 }, "username":"Norbi6", "registrationTime":"2022-07-01T17:58:10.554237Z" } ], "authData":{ "rpIdHash":"5edee97e57447bf8a06e4d5d7743d8631225a6012cc96979b620dc87728a574a", "flags":{ "value":5, "UP":true, "UV":true, "AT":false, "ED":false }, "signatureCounter":0, "extensions":null }, "username":"Norbi6", "sessionToken":"5rFaWptHgn2fkEaeoyluK3JiBoHpG6B3bhvKOWoeJjI" }
resident credential:
{ "success":true, "request":{ // very same structure that is in the response // for the POST /api/v1/authenticate call }, "response":{ // very same structure that is in the request // for the POST /api/v1/authenticate/finish call }, "registrations":[ { "userIdentity":{ "name":"norbi22", "displayName":"norbi22", "id":"cDNuvwcp5pkgfwU0y5kK_-yJutLtK5t2PJP_8Ct5vkw" }, "credentialNickname":"", "transports":[ "internal" ], "credential":{ "credentialId":"fkB33Lg5CGJutWwI2sCx4zuOkc3k69FUs4XDPOibpa7U3CTPyL55VbJm5gm0lCFpd4Ltz8Gp-7zFIJr6vV3K6PxLc0PchHFhXjGOeME-G44", "userHandle":"cDNuvwcp5pkgfwU0y5kK_-yJutLtK5t2PJP_8Ct5vkw", "publicKeyCose":"pQECAyYgASFYIExNEiVXRgcghsYkHfWX7V7MmdX6BMzSWJwXHuZdSKnmIlggiHvLLjyBkpEyHlMbPaG7qqPQJfibtyeQFq5kP_nESlk", "signatureCount":0 }, "username":"norbi22", "registrationTime":"2022-07-07T17:31:39.151539Z" } ], "authData":{ "rpIdHash":"4716147db71985f2a84939a25307a36a8dbae02be20941afd9cd6293a5db2fd2", "flags":{ "value":5, "UP":true, "UV":true, "AT":false, "ED":false }, "signatureCounter":0, "extensions":null }, "username":"norbi22", "sessionToken":"LAlt2lPOsbk7vR4xfFnnOBIWbl3jpfvObToo_AkwvMg" }
POST /api/v1/action/deregister
deregister user (remove user from db)
user is identified with its credential ID
DELETE /api/v1/delete-account
similar to POST /api/v1/action/deregister, but user is identified by username
POST /api/v1/fetch-user-keys
returns a list of all public keys associated to the username
request:
{ "username" : "myName"}
response - list of tuples in the format {credentialId, publicKeyCose}
POST /api/v1/fetch-key
returns the public key associated with the username and credentialId
request:
{ "userHandle": "PRHXZ9HMwvl3eRSuzZ16XY7gr0AOfl6OuGr0geOxGY0", "credentialId": "Zl1TeirsX-G_i2n5TfDfMA5w4_k9d5AL-t9YtdifItA" }
response - tuple in the format {credentialId, publicKeyCose}
Backend API - protected-service
Deployed at https://auth-1-abc.sandbox.safibank.online/
POST /api/v1/send-message-webauthn
send an arbitrary text message, signed using the client authenticator, from a particular username (and credentialId) to the server
leverages signature mechanism of client-side WebAuthn, and verification on server-side
the text message to be sent is fed into the authenticator as the “challenge” string
request - the structure should exactly match the output of the authenticator:
{ "credential": { "type": "public-key", "id": "Zl1TeirsX-G_i2n5TfDfMA5w4_k9d5AL-t9YtdifItA", "rawId": "Zl1TeirsX-G_i2n5TfDfMA5w4_k9d5AL-t9YtdifItA", "response": { "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiWlhsS2FHSkhZMmxQYVVwVFZYcEpNVTVwU1hOSmJVNTVZVmhSYVU5c2MybGtXRTVzWTIwMWFHSlhWV2xNUTBwcVkyMVdhMXBYTlRCaFYwWnpVMWRSYVZoVGQybGtXRTVzWTIwMWFHSlhWV2xQYVVwM1lqTkNhR041U1hOSmJVNTVXbGRTYkdKdVVuQlpWM2hLV2tOSk5rbHNjSE5OVmxKc1lWaEtlbGRETVVoWU1tdDVZbXBXVlZwclVtMVVWVVV4WkhwU1ptRjZiR3RPVlVaTlRGaFJOVmRZVW10aFYxcEtaRVZGYVdaUlBUMHVaWGxLZEZwWVRucFpWMlJzU1dwdmFXUXlWbTFqYlZWcFpsRTlQUSIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0Ojg0NDMiLCJjcm9zc09yaWdpbiI6ZmFsc2V9", "authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAAAw", "signature": "uaTxv9YB1usmsz1ID_ZA5609fo_09ds5NZ7J0x99e0jw8FvOw3pPFTsI9aa4SO2Au7mSMAtrKv3FkAPUnL1LExWXHUHu_UCiDwjxqL48H0NY5f4N0U7W57PsDepDVR0NGt4N7MZGEkqgMz0qM0eeTqv3Q6unLKKi5lPX55WU2fiOrVUWsTUuE2Sd22bXXmBqtORBT-N-x8JmnLGqkAzPaOTFywvmoRqMk6HdffZbKPyRTAnmT32k360OzghBCI1v8FIyGiGb_Qkg2Mjq6CE6NB4Fltcnte0FfW2TIx_O8UyPwA9ryBGWro3qucIshmI6c7Prih-ScL4eVI9aJjYbqA", "userHandle": "PRHXZ9HMwvl3eRSuzZ16XY7gr0AOfl6OuGr0geOxGY0" }, "clientExtensionResults": { "appid": false } } }
response:
{ "success": true, -> the server was able to decode the message "verified": true, -> the signature verification was successful "message": "the_string_sent_from_the_client_side", -> the actual message string "error": "" -> any errors encountered; empty string means no error }
Attachments:
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus arch.drawio.tmp (application/vnd.jgraph.mxfile)
Portunus arch.drawio (application/vnd.jgraph.mxfile)
Portunus arch.drawio.png (image/png)
Portunus arch.drawio-f70fdfb13757fb92cd1904b501095668eff9a5f3.png (image/png)
Portunus arch.drawio-f70fdfb13757fb92cd1904b501095668eff9a5f3.png (image/png)
Portunus Architecture.drawio (application/vnd.jgraph.mxfile)
Portunus Architecture.drawio.png (image/png)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
Portunus Architecture.drawio (application/vnd.jgraph.mxfile)
Portunus Architecture.drawio.png (image/png)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
Portunus Architecture.drawio (application/vnd.jgraph.mxfile)
Portunus Architecture.drawio.png (image/png)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
Portunus Architecture.drawio (application/vnd.jgraph.mxfile)
Portunus Architecture.drawio.png (image/png)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus data flow.drawio.tmp (application/vnd.jgraph.mxfile)
~drawio~6203b4b4e5caff0070e2aa9c~Portunus data flow.drawio.tmp (application/vnd.jgraph.mxfile)
plantuml_1657027776791.svg (image/svg+xml)
plantuml_1657027776791 (text/plain)
plantuml_1657027776791.png (image/png)
plantuml_1657027776791 (text/plain)
plantuml_1657027776791.svg (image/svg+xml)
plantuml_1657027776791.png (image/png)
plantuml_1657027776791.svg (image/svg+xml)
plantuml_1657027776791 (text/plain)
plantuml_1657027776791.png (image/png)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
Portunus Architecture.drawio (application/vnd.jgraph.mxfile)
Portunus Architecture.drawio.png (image/png)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
Portunus Architecture.drawio (application/vnd.jgraph.mxfile)
Portunus Architecture.drawio.png (image/png)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
Portunus Architecture.drawio (application/vnd.jgraph.mxfile)
Portunus Architecture.drawio.png (image/png)
Portunus PoC System Diagram.drawio (application/vnd.jgraph.mxfile)
Portunus PoC System Diagram.drawio.png (image/png)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
Portunus PoC System Diagram.drawio (application/vnd.jgraph.mxfile)
Portunus PoC System Diagram.drawio.png (image/png)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
Portunus PoC System Diagram.drawio (application/vnd.jgraph.mxfile)
Portunus PoC System Diagram.drawio.png (image/png)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
Portunus PoC System Diagram.drawio (application/vnd.jgraph.mxfile)
Portunus PoC System Diagram.drawio.png (image/png)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus PoC System Diagram.drawio.tmp (application/vnd.jgraph.mxfile)
Portunus PoC System Diagram.drawio (application/vnd.jgraph.mxfile)
Portunus PoC System Diagram.drawio.png (image/png)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
Portunus Architecture.drawio (application/vnd.jgraph.mxfile)
Portunus Architecture.drawio.png (image/png)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
Portunus Architecture.drawio (application/vnd.jgraph.mxfile)
Portunus Architecture.drawio.png (image/png)
~Portunus Architecture.drawio.tmp (application/vnd.jgraph.mxfile)
Portunus Architecture.drawio (application/vnd.jgraph.mxfile)
Portunus Architecture.drawio.png (image/png)