The FedID Protocol
The below demonstrates the flow of creation of, and sign-in using, a FedID. The entire API structure can be found in a OpenAPI spec via Swagger at /api/v2
.
Creating a FedID
Request to supported domains
First the FedID App will request the available domains to use for usernames from the FedID Server
Request:
GET /api/v2/domains
Response:
{
"success": true,
"data": {
"domains": [
"domain1.ext"
]
}
}
Creating the DID
The FedID App then generates private/public key pairs for a device control key (for managing the DID), and a pair for recovery, and delivers those to the FedID Server to create the DID.
Request:
POST /api/v2/did/create
Data:
{
"shortName": "theuser@domain1.ext",
"control": "j6RHrlucLa7DnOrabIAe_Ec0QjgQ9oMQbGURJhAyJyE=",
"recoveryHash": "20b528ce3419b0731af25d0e79096937eb87cc5aa3f3361e"
}
Response:
{
"success": true,
"data": {
"didDoc": {
"id": "did:jlinc:h:did.domain1.ext:cf1fd4e244c4593d78e711b4cff22b55",
"version": "2.0",
"@context": [
"https://www.w3.org/ns/did/v1",
"https://didspec.jlinc.io/v2/ctx.jsonld"
],
"verificationMethod": [
{
"id": "did:jlinc:h:device:524634f6af0a0e8fa95b3b1cc2e505c9",
"type": "device",
"controller": "did:jlinc:h:did.domain1.ext:cf1fd4e244c4593d78e711b4cff22b55",
"key": "j6RHrlucLa7DnOrabIAe_Ec0QjgQ9oMQbGURJhAyJyE="
}
],
"capabilityDelegation": [],
"service": [
{
"id": "did:jlinc:h:did.domain1.ext:b8fb6d540dba8e6979f9429f7f2c04f6",
"type": "login",
"serviceEndpoint": "https://fedid.domain1.ext"
}
],
"created": "2024-10-15T11:49:06Z",
"updated": "2024-10-15T11:49:06Z",
"deactivated": false,
"shortName": "theuser@domain1.ext",
"recoveryHash": "20b528ce3419b0731af25d0e79096937eb87cc5aa3f3361e"
}
}
}
Signing the DID
To prove this DID is for this user, an update call is made after signing the DID with the private control key.
Request:
POST /api/v2/did/update
Data:
{
"didDoc": {
"id": "did:jlinc:h:did.domain1.ext:cf1fd4e244c4593d78e711b4cff22b55",
"version": "2.1",
"@context": [
"https://www.w3.org/ns/did/v1",
"https://didspec.jlinc.io/v2/ctx.jsonld"
],
"verificationMethod": [
{
"id": "did:jlinc:h:device:524634f6af0a0e8fa95b3b1cc2e505c9",
"type": "device",
"controller": "did:jlinc:h:did.domain1.ext:cf1fd4e244c4593d78e711b4cff22b55",
"key": "j6RHrlucLa7DnOrabIAe_Ec0QjgQ9oMQbGURJhAyJyE="
}
],
"capabilityDelegation": [],
"service": [
{
"id": "did:jlinc:h:did.domain1.ext:b8fb6d540dba8e6979f9429f7f2c04f6",
"type": "login",
"serviceEndpoint": "https://fedid.domain1.ext"
}
],
"created": "2024-10-15T11:49:06Z",
"updated": "2024-10-15T11:49:06Z",
"deactivated": false,
"shortName": "theuser@domain1.ext",
"recoveryHash": "20b528ce3419b0731af25d0e79096937eb87cc5aa3f3361e"
},
"publicKey": "j6RHrlucLa7DnOrabIAe_Ec0QjgQ9oMQbGURJhAyJyE=",
"signature": "3_dINtsTUKhtFlnJia4UqPIMOTVA-_co4SZF3Aw0_AU_OqfToHXSBBvfXuC9VtBbfEx8zpSeIElwblvxmytzAw=="
}
Response:
{
"success": true,
"data": {
"version": "2.1",
"@context": [
"https://www.w3.org/ns/did/v1",
"https://didspec.jlinc.io/v2/ctx.jsonld"
],
"id": "did:jlinc:h:did.domain1.ext:cf1fd4e244c4593d78e711b4cff22b55",
"shortName": "theuser@domain1.ext",
"verificationMethod": [
{
"id": "did:jlinc:h:device:524634f6af0a0e8fa95b3b1cc2e505c9",
"type": "device",
"controller": "did:jlinc:h:did.domain1.ext:cf1fd4e244c4593d78e711b4cff22b55",
"key": "j6RHrlucLa7DnOrabIAe_Ec0QjgQ9oMQbGURJhAyJyE="
}
],
"capabilityDelegation": [],
"service": [
{
"id": "did:jlinc:h:did.domain1.ext:b8fb6d540dba8e6979f9429f7f2c04f6",
"type": "login",
"serviceEndpoint": "https://fedid.domain1.ext"
}
],
"created": "2024-10-15T11:49:06Z",
"updated": "2024-10-15T11:49:06Z",
"deactivated": false,
"recoveryHash": "20b528ce3419b0731af25d0e79096937eb87cc5aa3f3361e",
"signature": "3_dINtsTUKhtFlnJia4UqPIMOTVA-_co4SZF3Aw0_AU_OqfToHXSBBvfXuC9VtBbfEx8zpSeIElwblvxmytzAw=="
}
}
Federating the DID
At this point, the DID is converted into an ActivityPub note object, and federated to any other conneted FedID Servers.
{
"id": "https://fedid.domain1.ext/s/eb71d8fa3bb11bd07e5cbedeeada8614e00642b0e5f35c97",
"type": "Create",
"actor": [
"https://fedid.domain1.ext/u/fedid"
],
"object": [
{
"id": "https://fedid.domain1.ext/o/06049c6e9c0a6fa4122f2361a813b038702a6fff0fe09d03",
"type": "Note",
"attributedTo": [
"https://fedid.domain1.ext/u/fedid"
],
"content": [
{
"didDoc": {
"id": "did:jlinc:h:did.domain1.ext:cf1fd4e244c4593d78e711b4cff22b55",
"version": "2.1",
"@context": [
"https://www.w3.org/ns/did/v1",
"https://didspec.jlinc.io/v2/ctx.jsonld"
],
"verificationMethod": [
{
"id": "did:jlinc:h:device:524634f6af0a0e8fa95b3b1cc2e505c9",
"type": "device",
"controller": "did:jlinc:h:did.domain1.ext:cf1fd4e244c4593d78e711b4cff22b55",
"key": "j6RHrlucLa7DnOrabIAe_Ec0QjgQ9oMQbGURJhAyJyE="
}
],
"capabilityDelegation": [],
"service": [
{
"id": "did:jlinc:h:did.domain1.ext:b8fb6d540dba8e6979f9429f7f2c04f6",
"type": "login",
"serviceEndpoint": "https://fedid.domain1.ext"
}
],
"created": "2024-10-15T11:49:06Z",
"updated": "2024-10-15T11:49:06Z",
"deactivated": false,
"shortName": "theuser@domain1.ext",
"recoveryHash": "20b528ce3419b0731af25d0e79096937eb87cc5aa3f3361e",
"signature": "3_dINtsTUKhtFlnJia4UqPIMOTVA-_co4SZF3Aw0_AU_OqfToHXSBBvfXuC9VtBbfEx8zpSeIElwblvxmytzAw=="
},
"publicKey": "j6RHrlucLa7DnOrabIAe_Ec0QjgQ9oMQbGURJhAyJyE="
}
],
"to": [
"https://fedid.domain2.ext/u/fedid"
]
}
],
"published": [
"2024-10-15T11:49:06.547Z"
],
"to": [
"https://fedid.domain2.ext/u/fedid"
],
"shares": {
"id": "https://fedid.domain1.ext/s/eb71d8fa3bb11bd07e5cbedeeada8614e00642b0e5f35c97/shares",
"type": "OrderedCollection",
"first": [
"https://fedid.domain1.ext/s/eb71d8fa3bb11bd07e5cbedeeada8614e00642b0e5f35c97/shares?page=true"
],
"totalItems": [
0
]
},
"likes": {
"id": "https://fedid.domain1.ext/s/eb71d8fa3bb11bd07e5cbedeeada8614e00642b0e5f35c97/likes",
"type": "OrderedCollection",
"first": [
"https://fedid.domain1.ext/s/eb71d8fa3bb11bd07e5cbedeeada8614e00642b0e5f35c97/likes?page=true"
],
"totalItems": [
0
]
}
}
Creating the OIDC Profile
Profile data is stored on the FedID Server.
Requesting the challenge code
To update the profile data, the FedID App requests a challenge code.
Request:
POST /api/v2/oidc/challenge
Data:
{
"id": "did:jlinc:h:did.domain1.ext:cf1fd4e244c4593d78e711b4cff22b55"
}
Response:
{
"success": true,
"data": {
"challenge": "3acb1fc6a84cac727405c5d8dc919fc699de1a72f475d8e37df690d0a2cb3149"
}
}
Signing the challenge code
The FedID App then signs the challenge to prove it is the user a retrieve the existing profile. As a profile does not yet exist, a false
success is returned.
Request:
POST /api/v2/oidc/profile
Data:
{
"id": "did:jlinc:h:did.domain1.ext:cf1fd4e244c4593d78e711b4cff22b55",
"challenge": "3acb1fc6a84cac727405c5d8dc919fc699de1a72f475d8e37df690d0a2cb3149",
"signature": "fyD3_r-t8wL1nnnarAI_ogY61zfixTLIP971oiwZNJPUgDz9SQXUlCjxOLq8GqIk2WHFLeRmTec1ANzj6KScCQ==",
"publicKey": "j6RHrlucLa7DnOrabIAe_Ec0QjgQ9oMQbGURJhAyJyE="
}
Response:
{
"success": false,
"error": "no profile data from get profile",
"status": 404
}
Creating the profile
The profile is then created, signed, and delivered to the FedID Server. This profile data is not federated on the network.
Request:
POST /api/v2/oidc/profile
Data:
{
"id": "did:jlinc:h:did.domain1.ext:cf1fd4e244c4593d78e711b4cff22b55",
"profile": {
"email": "theuser@domain.ext",
"addressCountry": "",
"addressLocality": "",
"addressPostalCode": "",
"addressRegion": "",
"addressStreetAddress": "",
"addressFormatted": "",
"birthdate": "",
"gender": "",
"givenName": "",
"middleName": "",
"familyName": "",
"nickName": "",
"phoneNumber": "",
"profile": "",
"website": "",
"zoneinfo": "",
"locale": "",
"picture": ""
},
"signature": "_0sqzliODuxDLxaDT0wEXnD9h1rFtfSi_T2xeVIiEfOvKgjUwZyl8TJRpa7KFyAFz26HX28rCRXjWEWwp-AXDg==",
"publicKey": "j6RHrlucLa7DnOrabIAe_Ec0QjgQ9oMQbGURJhAyJyE="
}
Response:
{
"success": true,
"status": 200,
"data": {
"profile": {
"email": "theuser@domain.ext",
"emailVerified": false,
"addressCountry": "",
"addressLocality": "",
"addressPostalCode": "",
"addressRegion": "",
"addressStreetAddress": "",
"addressFormatted": "",
"birthdate": "",
"gender": "",
"givenName": "",
"middleName": "",
"familyName": "",
"nickName": null,
"phoneNumber": "",
"phoneNumberVerified": false,
"profile": "",
"website": "",
"zoneinfo": null,
"locale": null,
"picture": "",
"createdTs": "2024-10-15T11:49:44.297Z",
"updatedTs": "2024-10-15T11:49:44.297Z"
}
}
}
Signing in with a FedID
FIDC request
An application makes an FIDC request to the FedID Server using any standard OIDC library. This moves them into the FIDC interaction, just like it would for OIDC.
Displaying the QR code
Instead of a standard username/password SSO screen for OIDC, FIDC supplies the interaction ID and a challenge code in a QR code format that can be scanned by the FedID App. It then makes repeated calls to the check login endpoint until the code is successfully scanned.
Request:
POST /api/v2/oidc/check-login
Data:
{
"uid": "sGymWd2dpK2oaLNRK3E5J",
"challenge": "c963865b54244fec0d0e72efc071612387728d290f4110aa6dcdfe3d42819001"
}
Response:
{
"success": false,
"error": "challenge not validated"
}
Scanning the QR code
Once the QR code is scanned by the FedID App, the signature is sent to the FedID Server.
Request:
POST /api/v2/oidc/login
Data:
{
"id": "did:jlinc:h:did.domain1.ext:cf1fd4e244c4593d78e711b4cff22b55",
"uid": "sGymWd2dpK2oaLNRK3E5J",
"publicKey": "j6RHrlucLa7DnOrabIAe_Ec0QjgQ9oMQbGURJhAyJyE=",
"challenge": "c963865b54244fec0d0e72efc071612387728d290f4110aa6dcdfe3d42819001",
"signature": "qQs+0KX/ODwUHN6h+UyvX6i1xU1WFG8PIwkGWNjsaBxffAVQpQr6dKWBi5UJy6KxVbGYcZFQBpVRYbF6P10gDQ=="
}
Response:
{
"success": true
}
Successful login
At this point, a success for the check login endpoint is returned, which moves the user on to the FIDC confirmation screen displaying the profile data that will be shared with the OIDC capable site.
Request:
POST /api/v2/oidc/check-login
Data:
{
"uid": "sGymWd2dpK2oaLNRK3E5J",
"challenge": "c963865b54244fec0d0e72efc071612387728d290f4110aa6dcdfe3d42819001"
}
Response:
{
"success": true
}
Finalizing the FIDC interaction
Once the user confirms sharing of the select profile data, the FIDC interaction is finalized, and the user is logged into the supporting site.