-
Notifications
You must be signed in to change notification settings - Fork 30
Docs/improvements #271
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Docs/improvements #271
Changes from 5 commits
7c7b46f
089eccd
02a2f0e
9e1886f
25859ee
0096057
11e290a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,19 +1,18 @@ | ||
| # Pubky Request Auth | ||
| # Pubky Auth | ||
|
|
||
| Pubky Request Auth is a protocol for using user's keypair to authenticate themselves to a 3rd party app and to authorize that app to access resources on the user's homeserver. | ||
| Pubky Auth is a protocol for using user's [root key](../concepts/rootkey.md) to authenticate themselves to a 3rd party app and to authorize that app to access resources on the user's [Homeserver](../concepts/homeserver.md). | ||
|
|
||
| ### Glossary | ||
| 1. **Authenticator**: An application holding the Keypair used in authentication. Eg [Pubky Ring](https://github.com/pubky/pubky-ring). | ||
| 1. **Pubky**: The public key (pubky) identifying the user. | ||
| 1. **Homeserver**: The public key (pubky) identifying the receiver of the authentication request, usually a server. | ||
| 1. **3rd Party App**: An application trying to get authorized to access some resources belonging to the **Pubky**. | ||
| 1. **Capabilities**: A list of strings specifying scopes and the actions that can be performed on them. | ||
| 1. **HTTP relay**: An independent HTTP relay (or the backend of the 3rd Party App) forwarding the [`AuthToken`](#authtoken-encoding) to the frontend. | ||
| ### glossary | ||
| 1. **User**: an entity owner of secret and public key and owner of assets which can be accessed on location identified by their public key. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to add the user here? Not sure this makes the docs clearer?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMHO as long as it does not add to confusion it is OK to have overly verbose docs |
||
| 2. **Authenticator**: an application holding the user's Keypair used in authentication. | ||
| 3. **Issuer**: an entity that issues access on behalf of **Pubky** to **3rd Party App**. | ||
| 4. **Pubky**: the public key (pubky) identifying the user secret key to which is owned by user. | ||
| 6. **3rd Party App**: an application trying to get authorized access to resources belonging to the **Pubky**. | ||
| 5. **Homeserver**: the public key (pubky) identified storage of resources the belong to **Pubky**. | ||
| 7. **Capabilities**: a list of strings specifying scopes and the actions that can be performed on them. | ||
| 8. **HTTP relay**: an independent HTTP relay (or the backend of the 3rd Party App) used for communication between **3rd Party App** and **Authenticator** . Since neither **Authenticator** nor **3rd Party App** are not extrernally reachabled. | ||
|
|
||
| ## Flow | ||
|
|
||
| Here's how is works: | ||
|
|
||
| ```mermaid | ||
| sequenceDiagram | ||
| participant User | ||
|
|
@@ -24,30 +23,39 @@ sequenceDiagram | |
|
|
||
| autonumber | ||
|
|
||
| 3rd Party App -->>3rd Party App : Generate a unique secret | ||
| 3rd Party App -->>3rd Party App : Generate | ||
| note over 3rd Party App ,3rd Party App: Unique 32 bytes `client_secret` | ||
| 3rd Party App ->>+HTTP Relay: Subscribe | ||
| note over 3rd Party App ,HTTP Relay: channel Id = hash(client secret) | ||
| 3rd Party App ->>Authenticator: Show QR code | ||
| note over 3rd Party App ,Authenticator: required Capabilities,<br/>relay url, and client secret | ||
| note over 3rd Party App ,HTTP Relay: `channel_id=hash(client_secret)` | ||
| 3rd Party App -->>3rd Party App : Compose PubkyAuth URL | ||
| note over 3rd Party App ,3rd Party App: `pubkyauth:///<br/>?relay=<HTTP Base URL><br/>&caps=<capabilities><br/>&secret=<base64url(client_secret)>` | ||
| 3rd Party App ->>Authenticator: Display QR code | ||
| note over 3rd Party App ,Authenticator: PubkyAuth URL | ||
| Authenticator-->>User: Display consent form | ||
| note over Authenticator ,User: Showing capabilities <br/> | ||
| User -->>Authenticator: Confirm consent | ||
| Authenticator-->>Authenticator: Sign AuthToken & encrypt with client secret | ||
| Authenticator->>HTTP Relay: Send encrypted AuthToken | ||
| note over Authenticator ,HTTP Relay: channel Id = hash(client secret) | ||
| HTTP Relay->>3rd Party App : Forward Encrypted AuthToken | ||
| note over User ,Authenticator: Assemble `AuthToken` | ||
| Authenticator-->>Authenticator: Sign `AuthToken` and encrypt with `client_secret` | ||
| Authenticator->>HTTP Relay: Send encrypted `AuthToken` | ||
| note over Authenticator ,HTTP Relay: `channel_id = hash(client_secret)` | ||
| HTTP Relay->>3rd Party App : Send `AuthToken` via subscription | ||
| note over HTTP Relay ,3rd Party App: `channel_id = hash(client_secret)` | ||
| HTTP Relay->>-Authenticator: Ok | ||
| 3rd Party App -->>3rd Party App : Decrypt AuthToken & Resolve user's homeserver | ||
| 3rd Party App ->>+Homeserver: Send AuthToken | ||
| Homeserver-->>Homeserver: Verify AuthToken | ||
| Homeserver->>-3rd Party App : Return SessionId | ||
| note over HTTP Relay ,Authenticator: Confirm forward | ||
| 3rd Party App -->>3rd Party App : Decrypt `AuthToken` | ||
| note over 3rd Party App ,3rd Party App : extract users pubky and resolve user's homeserver | ||
| 3rd Party App ->>+Homeserver: Send `AuthToken` | ||
| Homeserver-->>Homeserver: Verify `AuthToken` | ||
| note over Homeserver ,Homeserver : Verify timestamp and signature. <br/> Associate capabilities with session | ||
| Homeserver->>-3rd Party App : Return `session_id` | ||
| 3rd Party App ->>+Homeserver: Request resources | ||
| Homeserver-->>Homeserver: Check Session capabilities | ||
| Homeserver ->>-3rd Party App: Ok | ||
| ``` | ||
|
|
||
| 1. `3rd Party App` generates a unique (32 bytes) `client_secret`. | ||
| 1. `3rd Party App` uses the `base64url(hash(client_secret))` as a `channel_id` and subscribe to that channel on the `HTTP Relay` it is using. | ||
| 1. `3rd Party App` formats a Pubky Auth url: | ||
| 2. `3rd Party App` uses the `base64url(hash(client_secret))` as a `channel_id` and subscribe to that channel on the `HTTP Relay` it is using. | ||
| 3. `3rd Party App` formats a Pubky Auth url: | ||
| ``` | ||
| pubkyauth:/// | ||
| ?relay=<HTTP Relay base (without channel_id)> | ||
|
|
@@ -62,18 +70,20 @@ pubkyauth:/// | |
| &secret=mAa8kGmlrynGzQLteDVW6-WeUGnfvHTpEmbNerbWfPI | ||
| ``` | ||
| and finally show that URL as a QR code to the user. | ||
| 1. The `Authenticator` app scans that QR code, parses the URL and shows a consent form for the user. | ||
| 1. The user decides whether or not to grant these capabilities to the `3rd Party App`. | ||
| 1. If the user approves, the `Authenticator` uses their Keypair to sign an [`AuthToken`](#authtoken-encoding), then encrypts that token with the `client_secret`. The `channel_id` is then calculated by hashing that secret and the encrypted token is sent to the callback url, which is the `relay` + `channel_id`. | ||
| 1. `HTTP Relay` forwards the encrypted `AuthToken` to the `3rd Party App` frontend and confirms the delivery with the `Authenticator`. | ||
| 1. `3rd Party App` decrypts the AuthToken using its `client_secret`, reads the `pubky` in it and sends it to their `Homeserver` to obtain a session. | ||
| 1. `Homeserver` verifies the session and stores the corresponding `capabilities`. | ||
| 1. `Homeserver` returns a session Id to the frontend to use in subsequent requests. | ||
| 1. `3rd Party App` uses the session Id to access some resource at the Homeserver. | ||
| 1. `Homeserver` checks the session capabilities to see if it is allowed to access that resource. | ||
| 1. `Homeserver` responds to the `3rd Party App` with the resource. | ||
|
|
||
| ### AuthToken encoding | ||
|
|
||
| 4. The `Authenticator` app scans that QR code, parse the URL, and show a consent form for the user. | ||
| 5. The user decides whether or not to grant these capabilities to the `3rd Party App`. | ||
| 7. If the user approves, the `Authenticator` then uses their Keypair, to sign an [AuthToken](#authtoken), and encrypt it with the `client_secret`, then calculate the `channel_id` by hashing `client_secret`, and send encrypted token to the callback url(`relay` + `channel_id`). | ||
| 8. `HTTP Relay` forwards the encrypted AuthToken to the `3rd Party App` frontend. | ||
| 9. `HTTP Relay` confirms the delivery with the `Authenticator` | ||
| 10. `3rd Party App` decrypts the AuthToken using its `client_secret`, read the `pubky` in it, and send it to their `homeserver` to obtain a session. | ||
| 11. `Homeserver` verifies the session and stores the corresponding `capabilities`. | ||
| 12. `Homeserver` returns a session id to the frontend to use in subsequent requests. | ||
| 13. `3rd Party App` uses the session id to access some resource at the Homeserver. | ||
| 14. `Homeserver` checks the session capabilities to see if it is allowed to access that resource. | ||
| 15. `Homeserver` responds to the `3rd Party App` with the resource. | ||
|
|
||
| ## AuthToken encoding | ||
| ```abnf | ||
| AuthToken = signature namespace version timestamp pubky capabilities | ||
|
|
||
|
|
@@ -97,22 +107,24 @@ action = "r" / "w" ; Read or write (more actions can be specified later) | |
| ### AuthToken verification | ||
| To verify a token the `Homeserver` should: | ||
| 1. Check the 75th byte (version) and make sure it is `0` for this spec. | ||
| 1. Deserialize the token | ||
| 1. Verify that the `timestamp` is within a window from the local time: the default should be 45 seconds in the past and 45 seconds in the future to handle latency and drifts. | ||
| 1. Verify that the `pubky` is the signer of the `signature` over the rest of the serialized token after the signature (`serialized_token[65..]`). | ||
| 1. To avoid reuse of the token the `Homeserver` should consider the `timestamp` and `pubky` (`serialized_token[75..115]`) as a unique sortable ID, and store it in a sortable key value store, rejecting any token that has the same ID, and removing all IDs that start with a timestamp that is older than the window mentioned in step 3. | ||
| 2. Deserialize the token | ||
| 3. Verify that the `timestamp` is within a window from the local time: the default should be 45 seconds in the past and 45 seconds in the future to handle latency and drifts. | ||
| 4. Verify that the `pubky` is the signer of the `signature` over the rest of the serialized token after the signature (`serialized_token[65..]`). | ||
| 5. To avoid reuse of the token the `Homeserver` should consider the `timestamp` and `pubky` (`serialized_token[75..115]`) as a unique sortable ID, and store it in a sortable key value store, rejecting any token that has the same ID, and removing all IDs that start with a timestamp that is older than the window mentioned in step 3. | ||
|
|
||
| ## Unhosted Apps and Relays | ||
| Callback URLs work fine for full-stack applications. There are however many cases where you would want to develop an application without a backend, yet you still would like to let users sign in and bring their own backend. The idea of [Unhosted](https://unhosted.org/) applications is one of the main reasons we are developing Homeservers. | ||
|
|
||
| These applications will still need some [relay](https://httprelay.io/) to receive the `AuthToken` when the `Authenticator` sends it to the callback URL. Since the `AuthToken` is a bearer token these relays can intercept it and use it before the unhosted app. That is why we need to encrypt the `AuthToken` with a key that the relay doesn't have access to, and only shared between the app and the `Authenticator` | ||
|
|
||
| ## Limitations | ||
| ## Limitations of `v0` | ||
|
|
||
| ### No delegation | ||
| In version zero the `pubky` **is** the `issuer`, meaning that the `AuthToken` is signed by the same key of the `pubky`. This is to simplify the spec until we have a reason to keep the `issuer` keys even more secure than being in a mobile app used rarely to authenticate a browser session once in a while. | ||
| In version zero, the `pubky`/`User` IS the `issuer`, meaning that the `AuthToken` is signed by the same key of the `pubky`. This is to simplify the spec, until we have a reason to keep the `issuer` keys even more secure than being in a mobile app used rarely to authenticate a browser session once in a while. | ||
|
|
||
| Having an `issuer` that isn't exactly the `pubky` means the `issuer` themselves need a certificate of delegation signed by the `pubky`. The problem with that is that you can either lookup that certificate on the Homeserver (making the verification process async and possibly taking too long to timeout) or do what most TLS apps do right now and send the certificates chain with the token. The problem with this is that you then have to deal with the eternal problem of revocation, which basically also forces you to go lookup somewhere making the the verification process async and possibly taking too long before timeout. | ||
| Having an `issuer` that isn't exactly the `pubky` means the `issuer` themselves need a certificate of delegation signed by the `pubky`. This can be done in following ways: | ||
| - Lookup that certificate on the Homeserver. Which may make the verification process possibly taking too long to timeout | ||
| - Send the certificates chain with the token. Which brings on the problem of revocation.. | ||
|
|
||
| ### Expiration is out of scope | ||
| While the token itself can only be used for very brief period, it is immediately exchanged for another authentication mechanism (usually a session ID). Deciding the expiration date of that authentication, if any, is out of the scope of this spec. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.