Skip to content

Commit

Permalink
docs: OIDC and ConnectionlessOffer examples
Browse files Browse the repository at this point in the history
Signed-off-by: Curtish <[email protected]>
  • Loading branch information
curtis-h committed Sep 23, 2024
1 parent 97e05e7 commit dc70a65
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 109 deletions.
8 changes: 7 additions & 1 deletion demos/next/src/reducers/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,19 @@ const appSlice = createSlice({
builder.addCase(sendMessage.pending, (state, action) => {
state.agent.isSendingMessage = true;
state.agent.hasSentMessage = false;
let credentialFormat = SDK.Domain.CredentialType.Unknown;
try {
credentialFormat = action.meta.arg.message.credentialFormat;
}
catch {}

state.messages.push({
...action.meta.arg.message,
isAnswering: true,
hasAnswered: false,
error: null,
body: action.meta.arg.message.body,
credentialFormat: action.meta.arg.message.credentialFormat
credentialFormat
})
})

Expand Down
28 changes: 28 additions & 0 deletions docs/examples/ConnectionlessOffer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Edge SDK Connectionless Credential Offer

## Flow
1. Obtain a Connectionless Credential Offer from an Issuer.

A Connectionless Credential Offer is an Out of Band Invitation with a Credential Offer Attachment.
This should be a URI with a single query parameter `_oob`, which is an encoded JSON.
It should look similar to:

```
https://my.domain.com/path?_oob=eyJpZCI6ImY5NmUzNjk5LTU5MWMtNGFlNy1iNWU2LTZlZmU2ZDI2MjU1YiIsInR5cGUiOiJodHRwczovL2RpZGNvbW0ub3JnL291dC1vZi1iYW5kLzIuMC9pbnZpdGF0aW9uIiwiZnJvbSI6ImRpZDpwZWVyOjIuRXo2TFNmc0tNZTh2U1NXa1lkWkNwbjRZVmlQRVJmZEdBaGRMQUdIZ3gyTEdKd2ZtQS5WejZNa3B3MWtTYWJCTXprQTN2NTl0UUZuaDNGdGtLeTZ4TGhMeGQ5UzZCQW9hQmcyLlNleUowSWpvaVpHMGlMQ0p6SWpwN0luVnlhU0k2SW1oMGRIQTZMeTh4T1RJdU1UWTRMakV1TXpjNk9EQTRNQzlrYVdSamIyMXRJaXdpY2lJNlcxMHNJbUVpT2xzaVpHbGtZMjl0YlM5Mk1pSmRmWDAiLCJib2R5Ijp7ImdvYWxfY29kZSI6Imlzc3VlLXZjIiwiZ29hbCI6IlRlc3QgT09CIiwiYWNjZXB0IjpbImRpZGNvbW0vdjIiXX0sImNyZWF0ZWRfdGltZSI6MTcyNDg1MTEzOSwiZXhwaXJlc190aW1lIjo5OTI0ODUxNDM5LCJhdHRhY2htZW50cyI6W3siaWQiOiIwMGNkYzkwYy05YTk5LTRjZGEtODdmZS00ZjRiMjU5NTExMmEiLCJtZWRpYV90eXBlIjoiYXBwbGljYXRpb24vanNvbiIsImRhdGEiOnsianNvbiI6eyJpZCI6IjY1NWU5YTJjLTQ4ZWQtNDU5Yi1iM2RhLTZiMzY4NjY1NTU2NCIsInR5cGUiOiJodHRwczovL2RpZGNvbW0ub3JnL2lzc3VlLWNyZWRlbnRpYWwvMy4wL29mZmVyLWNyZWRlbnRpYWwiLCJib2R5Ijp7ImdvYWxfY29kZSI6Ik9mZmVyIENyZWRlbnRpYWwiLCJjcmVkZW50aWFsX3ByZXZpZXciOnsidHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvaXNzdWUtY3JlZGVudGlhbC8zLjAvY3JlZGVudGlhbC1jcmVkZW50aWFsIiwiYm9keSI6eyJhdHRyaWJ1dGVzIjpbeyJuYW1lIjoiZmFtaWx5TmFtZSIsInZhbHVlIjoiV29uZGVybGFuZCJ9XX19fSwiYXR0YWNobWVudHMiOlt7ImlkIjoiODQwNDY3OGItOWEzNi00OTg5LWFmMWQtMGY0NDUzNDdlMGUzIiwibWVkaWFfdHlwZSI6ImFwcGxpY2F0aW9uL2pzb24iLCJkYXRhIjp7Impzb24iOnsib3B0aW9ucyI6eyJjaGFsbGVuZ2UiOiJhZDBmNDNhZC04NTM4LTQxZDQtOWNiOC0yMDk2N2JjNjg1YmMiLCJkb21haW4iOiJkb21haW4ifSwicHJlc2VudGF0aW9uX2RlZmluaXRpb24iOnsiaWQiOiI3NDhlZmE1OC0yYmNlLTQ0MGQtOTIxZi0yNTIwYTg0NDY2NjMiLCJpbnB1dF9kZXNjcmlwdG9ycyI6W10sImZvcm1hdCI6eyJqd3QiOnsiYWxnIjpbIkVTMjU2SyJdLCJwcm9vZl90eXBlIjpbXX19fX19LCJmb3JtYXQiOiJwcmlzbS9qd3QifV0sInRoaWQiOiJmOTZlMzY5OS01OTFjLTRhZTctYjVlNi02ZWZlNmQyNjI1NWIiLCJmcm9tIjoiZGlkOnBlZXI6Mi5FejZMU2ZzS01lOHZTU1drWWRaQ3BuNFlWaVBFUmZkR0FoZExBR0hneDJMR0p3Zm1BLlZ6Nk1rcHcxa1NhYkJNemtBM3Y1OXRRRm5oM0Z0a0t5NnhMaEx4ZDlTNkJBb2FCZzIuU2V5SjBJam9pWkcwaUxDSnpJanA3SW5WeWFTSTZJbWgwZEhBNkx5OHhPVEl1TVRZNExqRXVNemM2T0RBNE1DOWthV1JqYjIxdElpd2ljaUk2VzEwc0ltRWlPbHNpWkdsa1kyOXRiUzkyTWlKZGZYMCJ9fX1dfQ==
```


2. Ensure the validity of the Invitation with `Agent.parseInvitation`

`parseInvitation` decodes and validates the encoded Out of Band Invitation, plus attachments, returning an instance of `OutOfBandInvitation` on success. This OutOfBandInvitation will have a single Attachment for the Credential Offer.

```
const oob = await Agent.parseInvitation(rawOob);
```

3. Use `Agent.acceptInvitation` to handle the OutOfBandInvitation appropriately.
In this case, with an attached Credential Offer, the Credential Offer Message will be stored in Pluto.

```
await Agent.acceptInvitation(oob)
```
6 changes: 3 additions & 3 deletions docs/examples/ConnectionlessPresentation.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Cross-Platform Edge SDK Verification
# Edge SDK Connectionless Presentation
## Requirements
1. A working Identus Mediator and an Identus Cloud Agent.
2. A holder who already has a JWT Credential issued by a known issuer (prism:did) [Holder A]


> NOTE:
>
> Please follow the [Quick started guide](../../docs/quick-start) to complete steps 1, 2, 3
> Please follow the [Quick started guide](../../docs/quick-start) to complete steps 1, 2

## Flow
Expand Down Expand Up @@ -53,4 +53,4 @@ The Verifier will make this available to the holder in shape of QA code, link, e

2. The holder then opens its the Edge Agent Wallet SDK, "Connections tab" and will pase the invitation link in the field.

Once the invitation is parsed, the user will then be able to see a new Verification request, choose one of the available credentials and send the proof as the verifier requested.
Once the invitation is parsed, the user will then be able to see a new Verification request, choose one of the available credentials and send the proof as the verifier requested.
41 changes: 41 additions & 0 deletions docs/examples/OIDC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Edge SDK Connectionless Credential Offer

## Flow
1. Obtain a Connectionless Credential Offer from an Issuer.

A Credential Offer should be a URI with a single query parameter `credential_offer`, which is an encoded JSON.
It should look similar to:

```
openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22http%3A%2F%2Flocalhost%3A8090%2Foid4vci%2Fissuers%2F59104e60-3bf1-4e38-be58-4a219acf561b%22%2C%22credential_configuration_ids%22%3A%5B%22Example%22%5D%2C%22grants%22%3A%7B%22authorization_code%22%3A%7B%22issuer_state%22%3A%22aa10127f-c27e-4130-a0ed-659e3e1ad6ae%22%7D%7D%7D
```


2. Ensure the validity of the Offer with `Agent.parseCredentialOffer`

`parseCredentialOffer` decodes and validates the encoded credential_offer returning the JSON on success.

```
const offer = await Agent.parseCredentialOffer(rawOffer);
```

3. Create an AuthorizationRequest with `Agent.resolveCredentialOffer`

`resolveCredentialOffer` takes the parsed Offer, relevant client id and the desired redirect url,
and constructs an AuthorizationRequest. This AuthorizationRequest contains a url that goes to the Authorization Server
where the user can authorize the client to gain an access token.

```
const authRequest = await Agent.resolveCredentialOffer(offer, CLIENT_ID, REDIRECT_URL);
const urlString = authRequest.url.href;
```

4. Retrieve the Credential with `Agent.resolveCredentialRequest`

After successfully authorizing, the user will be redirected to the given redirect url.
Once there we use the full URL plus query parameters, the Offer and the AuthorizationRequest
to gain an access token and request the Credential (these steps are handled by `resolveCredentialRequest`).

```
const credential = await oidcAgent.resolveCredentialRequest(offer, authRequest, { callbackUrl: url });
```
10 changes: 8 additions & 2 deletions docs/examples/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,14 @@ Start a verification flow from an Edge Agent (holder wallet) and another holder
[Start tutorial](./SDKVerification.md)


### Connection less presentation
### Connectionless Credential Offer

How to handle a Connectionless Credential Offer.

[Start tutorial](./ConnectionlessOffer.md)

### Connectionless presentation

Trigger the verifier to generate an out of band verification request. It will work exactly as the didcomm connection invitation links

[Start tutorial](./ConnectionlessPresentation.md)
[Start tutorial](./ConnectionlessPresentation.md)
231 changes: 128 additions & 103 deletions docs/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,121 +3,146 @@ const fs = require('fs')

// @ts-check
const sidebars = {
tutorialsSidebar: [
tutorialsSidebar: [
{
label: 'Introduction',
type: "doc",
id: "sdk/README"
},
{
type: 'category',
label: 'Examples',
link: {
type: 'doc',
id: 'examples/index',
},
items: [
{
label: 'Introduction',
type: "doc",
id: "sdk/README"
type: 'doc',
label: "Backup",
id: 'examples/Backup',
},
{
type: 'category',
label: 'Examples',
link: {
type: 'doc',
id: 'examples/index',
},
items: [
{
type: 'doc',
label: "SDK Verification",
id: 'examples/SDKVerification',
}

]
type: 'doc',
label: "OIDC",
id: 'examples/OIDC',
},
{
type: 'doc',
label: "Verification",
id: 'examples/SDKVerification',
},
{
type: 'category',
label: 'Modules',
link: {
type: 'doc',
id: 'sdk/modules',
type: 'category',
label: 'Connectionless',
items: [
{
type: 'doc',
label: "Credential Offer",
id: 'examples/ConnectionlessOffer',
},
items: [
'sdk/classes/Apollo',
'sdk/classes/Castor',
'sdk/classes/Agent',
'sdk/classes/Mercury',
'sdk/classes/Pollux',
{
type: 'category',
label: 'Domain',
link: {
type: 'doc',
id: 'sdk/modules/Domain',
},
items: fs.readdirSync(path.resolve(__dirname, "./sdk"))
.reduce((menu, file) => {
const fileExtension = file.split(".")
if (fileExtension[fileExtension.length - 1] === "md") {
return menu
} else if (fs.lstatSync(path.resolve(__dirname, "./sdk", file)).isDirectory()) {
const currentFolder = file;
const files = fs.readdirSync(path.resolve(__dirname, "./sdk", currentFolder)).filter((file) => file !== "Domain.md" && file.includes("Domain."));

return [
...menu,
...files.map((filename) => {
{
type: 'doc',
label: "Presentation",
id: 'examples/ConnectionlessPresentation',
},
]
},
]
},
{
type: 'category',
label: 'Modules',
link: {
type: 'doc',
id: 'sdk/modules',
},
items: [
'sdk/classes/Apollo',
'sdk/classes/Castor',
'sdk/classes/Agent',
'sdk/classes/Mercury',
'sdk/classes/Pollux',
{
type: 'category',
label: 'Domain',
link: {
type: 'doc',
id: 'sdk/modules/Domain',
},
items: fs.readdirSync(path.resolve(__dirname, "./sdk"))
.reduce((menu, file) => {
const fileExtension = file.split(".")
if (fileExtension[fileExtension.length - 1] === "md") {
return menu
} else if (fs.lstatSync(path.resolve(__dirname, "./sdk", file)).isDirectory()) {
const currentFolder = file;
const files = fs.readdirSync(path.resolve(__dirname, "./sdk", currentFolder)).filter((file) => file !== "Domain.md" && file.includes("Domain."));

const fixFile = `sdk/${currentFolder}/${filename.replace(".md", "")}`
return {
label: fixFile.replace(`sdk/${currentFolder}/Domain.`, ""),
type: "doc",
id: fixFile
}
return [
...menu,
...files.map((filename) => {

})
]
}
return menu;
}, [])
},
{
type: 'category',
label: 'Reference',
link: {
type: 'generated-index',
title: 'SDK Reference',
description: 'All other exported classes, types, interfaces and references.'
},
items: fs.readdirSync(path.resolve(__dirname, "./sdk"))
.filter((file) => !file.includes("Domain."))
.reduce((menu, file) => {
const fileExtension = file.split(".")
if (fileExtension[fileExtension.length - 1] === "md") {
return menu
} else if (fs.lstatSync(path.resolve(__dirname, "./sdk", file)).isDirectory()) {
const currentFolder = file;
const files = fs.readdirSync(path.resolve(__dirname, "./sdk", currentFolder)).filter((file) => !file.includes("Domain."))
return [
...menu,
...files.map((filename) => {
const fixFile = `sdk/${currentFolder}/${filename.replace(".md", "")}`
return {
label: fixFile.replace(`sdk/${currentFolder}/`, ""),
type: "doc",
id: fixFile
}
const fixFile = `sdk/${currentFolder}/${filename.replace(".md", "")}`
return {
label: fixFile.replace(`sdk/${currentFolder}/Domain.`, ""),
type: "doc",
id: fixFile
}

})
]
}
return menu;
}, [])
}
]
})
]
}
return menu;
}, [])
},
{
type: 'category',
label: 'Decision records',
items: [
{
type: 'doc',
id: 'decisions/sdk-package-release',
}
type: 'category',
label: 'Reference',
link: {
type: 'generated-index',
title: 'SDK Reference',
description: 'All other exported classes, types, interfaces and references.'
},
items: fs.readdirSync(path.resolve(__dirname, "./sdk"))
.filter((file) => !file.includes("Domain."))
.reduce((menu, file) => {
const fileExtension = file.split(".")
if (fileExtension[fileExtension.length - 1] === "md") {
return menu
} else if (fs.lstatSync(path.resolve(__dirname, "./sdk", file)).isDirectory()) {
const currentFolder = file;
const files = fs.readdirSync(path.resolve(__dirname, "./sdk", currentFolder)).filter((file) => !file.includes("Domain."))
return [
...menu,
...files.map((filename) => {
const fixFile = `sdk/${currentFolder}/${filename.replace(".md", "")}`
return {
label: fixFile.replace(`sdk/${currentFolder}/`, ""),
type: "doc",
id: fixFile
}

]
},
]
})
]
}
return menu;
}, [])
}
]
},
{
type: 'category',
label: 'Decision records',
items: [
{
type: 'doc',
id: 'decisions/sdk-package-release',
}

]
},
]
}

module.exports = sidebars

0 comments on commit dc70a65

Please sign in to comment.