|
|
|
@ -24,7 +24,7 @@ In order for the new device to be fully set up, it needs to exchange information
|
|
|
|
|
|
|
|
|
|
|
|
It is proposed that an HTTP-based protocol be used to establish an ephemeral bi-directional communication session over
|
|
|
|
It is proposed that an HTTP-based protocol be used to establish an ephemeral bi-directional communication session over
|
|
|
|
which the two devices can exchange the necessary data. This session is described as "insecure" as it provides no
|
|
|
|
which the two devices can exchange the necessary data. This session is described as "insecure" as it provides no
|
|
|
|
end-to-end confidentiality nor authenticity by itself—these are layered on top of it.
|
|
|
|
end-to-end confidentiality nor authenticity by itself---these are layered on top of it.
|
|
|
|
|
|
|
|
|
|
|
|
#### High-level description
|
|
|
|
#### High-level description
|
|
|
|
|
|
|
|
|
|
|
|
@ -242,7 +242,7 @@ Access-Control-Expose-Headers: ETag
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
To support usage from web browsers the rendezvous URLs should allow CORS requests from any origin and expose the headers
|
|
|
|
To support usage from web browsers the rendezvous URLs should allow CORS requests from any origin and expose the headers
|
|
|
|
which aren’t on the CORS [request header](https://developer.mozilla.org/en-US/docs/Glossary/CORS-safelisted_request_header) and
|
|
|
|
which aren't on the CORS [request header](https://developer.mozilla.org/en-US/docs/Glossary/CORS-safelisted_request_header) and
|
|
|
|
[response header](https://developer.mozilla.org/en-US/docs/Glossary/CORS-safelisted_response_header) safelists:
|
|
|
|
[response header](https://developer.mozilla.org/en-US/docs/Glossary/CORS-safelisted_response_header) safelists:
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
```http
|
|
|
|
@ -394,8 +394,8 @@ with an empty payload. It parses the **id** and **server** received.
|
|
|
|
|
|
|
|
|
|
|
|
Device G displays a QR code containing:
|
|
|
|
Device G displays a QR code containing:
|
|
|
|
|
|
|
|
|
|
|
|
- its public key **Gp**
|
|
|
|
- Its public key **Gp**
|
|
|
|
- the insecure rendezvous session **URL**
|
|
|
|
- The insecure rendezvous session **URL**
|
|
|
|
- An indicator (the **intent**) to say if this is a new device which wishes to "initiate" a login, or an existing device
|
|
|
|
- An indicator (the **intent**) to say if this is a new device which wishes to "initiate" a login, or an existing device
|
|
|
|
that wishes to "reciprocate" a login
|
|
|
|
that wishes to "reciprocate" a login
|
|
|
|
- If the intent is to reciprocate a login, then the **homeserver base URL**
|
|
|
|
- If the intent is to reciprocate a login, then the **homeserver base URL**
|
|
|
|
@ -618,9 +618,9 @@ Since Device G has no way of authenticating Device S, an attacker present for th
|
|
|
|
attempt to mimic Device S in order to get their Device S signed in instead.
|
|
|
|
attempt to mimic Device S in order to get their Device S signed in instead.
|
|
|
|
|
|
|
|
|
|
|
|
- In step 3, Specter can shoulder surf the QR code scanning to obtain **Gp**.
|
|
|
|
- In step 3, Specter can shoulder surf the QR code scanning to obtain **Gp**.
|
|
|
|
- In step 4, Specter can intercept S’s payload and replace it with a payload of their own, replacing **Sp** with its
|
|
|
|
- In step 4, Specter can intercept S's payload and replace it with a payload of their own, replacing **Sp** with its
|
|
|
|
own key.
|
|
|
|
own key.
|
|
|
|
- The attack is only thwarted in step 7, because Device S won’t ever display the indicator of success to the user. The
|
|
|
|
- The attack is only thwarted in step 7, because Device S won't ever display the indicator of success to the user. The
|
|
|
|
user then must cancel the process on Device G, preventing it from sharing any sensitive material.
|
|
|
|
user then must cancel the process on Device G, preventing it from sharing any sensitive material.
|
|
|
|
|
|
|
|
|
|
|
|
### The OIDC login part and set up of E2EE
|
|
|
|
### The OIDC login part and set up of E2EE
|
|
|
|
@ -657,9 +657,9 @@ homeserver specified:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"type": "m.login.protocols",
|
|
|
|
"type": "m.login.protocols",
|
|
|
|
"protocols": ["device_authorization_grant"],
|
|
|
|
"protocols": ["device_authorization_grant"],
|
|
|
|
"homeserver": "https://synapse-oidc.lab.element.dev"
|
|
|
|
"homeserver": "https://synapse-oidc.lab.element.dev"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
@ -684,7 +684,7 @@ With response like:
|
|
|
|
Content-Type: application/json
|
|
|
|
Content-Type: application/json
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"issuer": "https://auth-oidc.lab.element.dev/"
|
|
|
|
"issuer": "https://auth-oidc.lab.element.dev/"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
@ -706,22 +706,22 @@ With response like:
|
|
|
|
Content-Type: application/json
|
|
|
|
Content-Type: application/json
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"issuer": "https://auth-oidc.lab.element.dev/",
|
|
|
|
"issuer": "https://auth-oidc.lab.element.dev/",
|
|
|
|
"authorization_endpoint": "https://auth-oidc.lab.element.dev/authorize",
|
|
|
|
"authorization_endpoint": "https://auth-oidc.lab.element.dev/authorize",
|
|
|
|
"token_endpoint": "https://auth-oidc.lab.element.dev/oauth2/token",
|
|
|
|
"token_endpoint": "https://auth-oidc.lab.element.dev/oauth2/token",
|
|
|
|
"jwks_uri": "https://auth-oidc.lab.element.dev/oauth2/keys.json",
|
|
|
|
"jwks_uri": "https://auth-oidc.lab.element.dev/oauth2/keys.json",
|
|
|
|
"registration_endpoint": "https://auth-oidc.lab.element.dev/oauth2/registration",
|
|
|
|
"registration_endpoint": "https://auth-oidc.lab.element.dev/oauth2/registration",
|
|
|
|
"scopes_supported": ["openid", "email"],
|
|
|
|
"scopes_supported": ["openid", "email"],
|
|
|
|
"response_types_supported": [...],
|
|
|
|
"response_types_supported": [...],
|
|
|
|
"response_modes_supported": [...],
|
|
|
|
"response_modes_supported": [...],
|
|
|
|
"grant_types_supported": [
|
|
|
|
"grant_types_supported": [
|
|
|
|
"authorization_code",
|
|
|
|
"authorization_code",
|
|
|
|
"refresh_token",
|
|
|
|
"refresh_token",
|
|
|
|
"client_credentials",
|
|
|
|
"client_credentials",
|
|
|
|
"urn:ietf:params:oauth:grant-type:device_code"
|
|
|
|
"urn:ietf:params:oauth:grant-type:device_code"
|
|
|
|
],
|
|
|
|
],
|
|
|
|
...
|
|
|
|
...
|
|
|
|
"device_authorization_endpoint": "https://auth-oidc.lab.element.dev/oauth2/device"
|
|
|
|
"device_authorization_endpoint": "https://auth-oidc.lab.element.dev/oauth2/device"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
@ -748,12 +748,12 @@ With response like:
|
|
|
|
Content-Type: application/json
|
|
|
|
Content-Type: application/json
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS",
|
|
|
|
"device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS",
|
|
|
|
"user_code": "123456",
|
|
|
|
"user_code": "123456",
|
|
|
|
"verification_uri": "https://auth-oidc.lab.element.dev/link",
|
|
|
|
"verification_uri": "https://auth-oidc.lab.element.dev/link",
|
|
|
|
"verification_uri_complete": "https://auth-oidc.lab.element.dev/link?code=123456",
|
|
|
|
"verification_uri_complete": "https://auth-oidc.lab.element.dev/link?code=123456",
|
|
|
|
"expires_in": 1800,
|
|
|
|
"expires_in": 1800,
|
|
|
|
"interval": 5
|
|
|
|
"interval": 5
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
@ -770,13 +770,13 @@ indicates that want to use protocol `device_authorization_grant` along with the
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"type": "m.login.protocol",
|
|
|
|
"type": "m.login.protocol",
|
|
|
|
"protocol": "device_authorization_grant",
|
|
|
|
"protocol": "device_authorization_grant",
|
|
|
|
"device_authorization_grant": {
|
|
|
|
"device_authorization_grant": {
|
|
|
|
"verification_uri": "https://auth-oidc.lab.element.dev/link",
|
|
|
|
"verification_uri": "https://auth-oidc.lab.element.dev/link",
|
|
|
|
"verification_uri_complete": "https://auth-oidc.lab.element.dev/link?code=123456"
|
|
|
|
"verification_uri_complete": "https://auth-oidc.lab.element.dev/link?code=123456"
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"device_id": "ABCDEFGH"
|
|
|
|
"device_id": "ABCDEFGH"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
@ -928,10 +928,10 @@ Then we continue with the actual login:
|
|
|
|
On receipt of the `m.login.protocol_accepted` message:
|
|
|
|
On receipt of the `m.login.protocol_accepted` message:
|
|
|
|
|
|
|
|
|
|
|
|
- In accordance with [RFC8628](https://datatracker.ietf.org/doc/html/rfc8628#section-3.3.1) the new device must display
|
|
|
|
- In accordance with [RFC8628](https://datatracker.ietf.org/doc/html/rfc8628#section-3.3.1) the new device must display
|
|
|
|
the user_code in order that the user can confirm it on the OIDC Provider if required.
|
|
|
|
the `user_code` in order that the user can confirm it on the OIDC Provider if required.
|
|
|
|
- The new device then starts to poll the OIDC Provider by making
|
|
|
|
- The new device then starts to poll the OIDC Provider by making
|
|
|
|
[Device Access Token Requests](https://datatracker.ietf.org/doc/html/rfc8628#section-3.4) using the interval and bounded
|
|
|
|
[Device Access Token Requests](https://datatracker.ietf.org/doc/html/rfc8628#section-3.4) using the interval and bounded
|
|
|
|
by expires_in.
|
|
|
|
by `expires_in`.
|
|
|
|
|
|
|
|
|
|
|
|
*New device => OIDC Provider via HTTP*
|
|
|
|
*New device => OIDC Provider via HTTP*
|
|
|
|
|
|
|
|
|
|
|
|
@ -962,7 +962,7 @@ and expecting to receive an HTTP 404 response.
|
|
|
|
If the device already exists then the login request should be rejected with an `m.login.failure` and reason `device_already_exists`.
|
|
|
|
If the device already exists then the login request should be rejected with an `m.login.failure` and reason `device_already_exists`.
|
|
|
|
|
|
|
|
|
|
|
|
If no existing device was found then the existing device opens the `verification_uri_complete` - falling back to the
|
|
|
|
If no existing device was found then the existing device opens the `verification_uri_complete` - falling back to the
|
|
|
|
`verification_uri`, if `verification_uri_complete` isn’t present - in a system browser.
|
|
|
|
`verification_uri`, if `verification_uri_complete` isn't present - in a system browser.
|
|
|
|
|
|
|
|
|
|
|
|
Ideally this is in a trusted/secure environment where the cookie jar and password manager features are available. e.g.
|
|
|
|
Ideally this is in a trusted/secure environment where the cookie jar and password manager features are available. e.g.
|
|
|
|
on iOS this could be a `ASWebAuthenticationSession`
|
|
|
|
on iOS this could be a `ASWebAuthenticationSession`
|
|
|
|
@ -973,7 +973,7 @@ The existing device then sends an acknowledgement message to let the other devic
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"type": "m.login.protocol_accepted"
|
|
|
|
"type": "m.login.protocol_accepted"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
@ -1050,7 +1050,7 @@ with the corresponding device_id (from the `m.login.protocol` message).
|
|
|
|
It does so by calling [GET /_matrix/client/v3/devices/<device_id>](https://spec.matrix.org/v1.9/client-server-api/#get_matrixclientv3devicesdeviceid)
|
|
|
|
It does so by calling [GET /_matrix/client/v3/devices/<device_id>](https://spec.matrix.org/v1.9/client-server-api/#get_matrixclientv3devicesdeviceid)
|
|
|
|
and expecting to receive an HTTP 200 response.
|
|
|
|
and expecting to receive an HTTP 200 response.
|
|
|
|
|
|
|
|
|
|
|
|
If the device isn’t immediately visible it can repeat the `GET` request for up to, say, 10 seconds to allow for any latency.
|
|
|
|
If the device isn't immediately visible it can repeat the `GET` request for up to, say, 10 seconds to allow for any latency.
|
|
|
|
|
|
|
|
|
|
|
|
If no device is found then the process should be stopped.
|
|
|
|
If no device is found then the process should be stopped.
|
|
|
|
|
|
|
|
|
|
|
|
@ -1060,17 +1060,17 @@ The existing device sends a `m.login.secrets` message via the secure channel:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"type": "m.login.secrets",
|
|
|
|
"type": "m.login.secrets",
|
|
|
|
"cross_signing": {
|
|
|
|
"cross_signing": {
|
|
|
|
"master_key": "$base64_of_the_key",
|
|
|
|
"master_key": "$base64_of_the_key",
|
|
|
|
"self_signing_key": "$base64_of_the_key",
|
|
|
|
"self_signing_key": "$base64_of_the_key",
|
|
|
|
"user_signing_key": "$base64_of_the_key"
|
|
|
|
"user_signing_key": "$base64_of_the_key"
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"backup": {
|
|
|
|
"backup": {
|
|
|
|
"algorithm": "foobar",
|
|
|
|
"algorithm": "foobar",
|
|
|
|
"key": "base64_of_the_backup_recovery_key",
|
|
|
|
"key": "base64_of_the_backup_recovery_key",
|
|
|
|
"backup_version": "version_string"
|
|
|
|
"backup_version": "version_string"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
@ -1188,9 +1188,9 @@ Fields:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"type": "m.login.protocols",
|
|
|
|
"type": "m.login.protocols",
|
|
|
|
"protocols": ["device_authorization_grant"],
|
|
|
|
"protocols": ["device_authorization_grant"],
|
|
|
|
"homeserver": "https://matrix-client.matrix.org"
|
|
|
|
"homeserver": "https://matrix-client.matrix.org"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
@ -1213,13 +1213,13 @@ Example:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"type": "m.login.protocol",
|
|
|
|
"type": "m.login.protocol",
|
|
|
|
"protocol": "device_authorization_grant",
|
|
|
|
"protocol": "device_authorization_grant",
|
|
|
|
"device_authorization_grant": {
|
|
|
|
"device_authorization_grant": {
|
|
|
|
"verification_uri_complete": "https://id.matrix.org/device/abcde",
|
|
|
|
"verification_uri_complete": "https://id.matrix.org/device/abcde",
|
|
|
|
"verification_uri": "..."
|
|
|
|
"verification_uri": "..."
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"device_id": "ABCDEFGH"
|
|
|
|
"device_id": "ABCDEFGH"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
@ -1234,7 +1234,7 @@ Example:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"type":"m.login.protocol_accepted"
|
|
|
|
"type":"m.login.protocol_accepted"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
@ -1256,9 +1256,9 @@ Example:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"type":"m.login.failure",
|
|
|
|
"type":"m.login.failure",
|
|
|
|
"reason": "unsupported",
|
|
|
|
"reason": "unsupported",
|
|
|
|
"homeserver": "https://matrix-client.matrix.org"
|
|
|
|
"homeserver": "https://matrix-client.matrix.org"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
@ -1278,7 +1278,7 @@ Example:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"type":"m.login.declined"
|
|
|
|
"type":"m.login.declined"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
@ -1298,7 +1298,7 @@ Example:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"type": "m.login.success"
|
|
|
|
"type": "m.login.success"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
@ -1320,17 +1320,17 @@ Example:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"type": "m.login.secrets",
|
|
|
|
"type": "m.login.secrets",
|
|
|
|
"cross_signing": {
|
|
|
|
"cross_signing": {
|
|
|
|
"master_key": "$base64_of_the_key",
|
|
|
|
"master_key": "$base64_of_the_key",
|
|
|
|
"self_signing_key": "$base64_of_the_key",
|
|
|
|
"self_signing_key": "$base64_of_the_key",
|
|
|
|
"user_signing_key": "$base64_of_the_key"
|
|
|
|
"user_signing_key": "$base64_of_the_key"
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"backup": {
|
|
|
|
"backup": {
|
|
|
|
"algorithm": "foobar",
|
|
|
|
"algorithm": "foobar",
|
|
|
|
"key": "base64_of_the_backup_recovery_key",
|
|
|
|
"key": "base64_of_the_backup_recovery_key",
|
|
|
|
"backup_version": "version_string"
|
|
|
|
"backup_version": "version_string"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|