-
Notifications
You must be signed in to change notification settings - Fork 216
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Document OAuth2 client credentials flow for HTTP connections.
Signed-off-by: Yufei Cai <yufei.cai@bosch.io>
- Loading branch information
Showing
4 changed files
with
323 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
211 changes: 211 additions & 0 deletions
211
documentation/src/main/resources/_posts/2021-11-03-oauth2.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
--- | ||
title: "Support for OAuth2 client credentials flow for HTTP connections" | ||
published: true | ||
permalink: 2021-11-03-oauth2.html | ||
layout: post | ||
author: yufei_cai | ||
tags: [blog, architecture, connectivity] | ||
hide_sidebar: true | ||
sidebar: false | ||
toc: true | ||
--- | ||
|
||
The upcoming release of Eclipse Ditto **version 2.2.0** supports HTTP connections that authenticate their requests | ||
via OAuth2 client credentials flow as described in | ||
[section 4.4 of RFC-6749](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4). | ||
|
||
Detailed information can be found at | ||
[Connectivity API > HTTP 1.1 protocol binding](connectivity-protocol-bindings-http.html#oauth2-client-credentials-flow). | ||
|
||
This blog post shows an example of publishing a twin event to an HTTP endpoint via OAuth2 client credentials flow. | ||
For simplicity, we will use `webhook.site` for both the token endpoint and the event publishing destination. | ||
Feel free to substitute them for real OAuth and HTTP servers. | ||
|
||
# Prerequisites | ||
|
||
This example requires 2 webhooks. We will use | ||
- `https://webhook.site/785e80cd-e6e6-452a-be97-a59c53edb4d9` for access token requests, and | ||
- `https://webhook.site/6148b899-736f-47e6-9382-90b1d721630e` for event publishing. | ||
|
||
Replace the webhook URIs by your own. | ||
|
||
# Configure the token endpoint | ||
|
||
Configure the token webhook to return a valid access token response. Here is an example for a token expiring | ||
at 00:00 on 1 January 3000. The field `expires_in` is an arbitrary big number not reflecting the actual expiration | ||
time of the access token. | ||
|
||
- Status code: 200 | ||
- Content type: `application/json` | ||
- Response body: | ||
```json | ||
{ | ||
"access_token": "ewogICJhbGciOiAiUlMyNTYiLAogICJ0eXAiOiAiSldUIgp9.ewogICJhdWQiOiBbXSwKICAiY2xpZW50X2lkIjogIm15LWNsaWVudC1pZCIsCiAgImV4cCI6IDMyNTAzNjgwMDAwLAogICJleHQiOiB7fSwKICAiaWF0IjogMCwKICAiaXNzIjogImh0dHBzOi8vbG9jYWxob3N0LyIsCiAgImp0aSI6ICI3ODVlODBjZC1lNmU2LTQ1MmEtYmU5Ny1hNTljNTNlZGI0ZDkiLAogICJuYmYiOiAwLAogICJzY3AiOiBbCiAgICAibXktc2NvcGUiCiAgXSwKICAic3ViIjogIm15LXN1YmplY3QiCn0.QUJD", | ||
"expires_in": 1048576, | ||
"scope": "my-scope", | ||
"token_type": "bearer" | ||
} | ||
``` | ||
|
||
The access token has the form `<headers>.<body>.<signature>`, where `<headers>` and `<body>` are base64-encoding | ||
of the headers and the body in JSON format, and `<signature>` is the base-64 encoded signature computed according | ||
to the issuer's key pair. Since the token webhook is not a real OAuth2 server, the signature in the example is a | ||
placeholder. The unencoded headers and body are as follows. | ||
|
||
### Headers | ||
|
||
```json | ||
{ | ||
"alg": "RS256", | ||
"typ": "JWT" | ||
} | ||
``` | ||
|
||
### Body | ||
|
||
```json | ||
{ | ||
"aud": [], | ||
"client_id": "my-client-id", | ||
"exp": 32503680000, | ||
"ext": {}, | ||
"iat": 0, | ||
"iss": "https://localhost/", | ||
"jti": "785e80cd-e6e6-452a-be97-a59c53edb4d9", | ||
"nbf": 0, | ||
"scp": [ | ||
"my-scope" | ||
], | ||
"sub": "my-subject" | ||
} | ||
``` | ||
|
||
# Create the connection | ||
|
||
[Create a connection](connectivity-manage-connections.html#create-connection) | ||
publishing twin events to the event publishing webhook using OAuth2 credentials. | ||
The `tokenEndpoint` field is set to the access token webhook. | ||
|
||
```json | ||
{ | ||
"id": "http_oauth2", | ||
"name": "http_oauth2", | ||
"connectionType": "http-push", | ||
"connectionStatus": "open", | ||
"uri": "https://webhook.site:443", | ||
"sources": [], | ||
"targets": [ | ||
{ | ||
"address": "POST:/6148b899-736f-47e6-9382-90b1d721630e", | ||
"topics": ["_/_/things/twin/events"], | ||
"authorizationContext": ["integration:ditto"], | ||
"headerMapping": {} | ||
} | ||
], | ||
"clientCount": 1, | ||
"processorPoolSize": 1, | ||
"failoverEnabled": true, | ||
"validateCertificates": true, | ||
"specificConfig": {}, | ||
"tags": [], | ||
"credentials": { | ||
"type": "oauth-client-credentials", | ||
"tokenEndpoint": "https://webhook.site/785e80cd-e6e6-452a-be97-a59c53edb4d9", | ||
"clientId": "my-client-id", | ||
"clientSecret": "my-client-secret", | ||
"requestedScopes": "my-scope" | ||
} | ||
} | ||
``` | ||
|
||
# Generate a thing-created event | ||
|
||
[Create a thing](http-api-doc.html#/Things/post_things) | ||
granting read access to the connection's subject. The thing-created event will be distributed | ||
to the connection for publishing. | ||
|
||
```json | ||
{ | ||
"_policy": { | ||
"entries": { | ||
"DEFAULT": { | ||
"subjects": { | ||
{%raw%} "{{ request:subjectId }}"{%endraw%}: { | ||
"type": "the creator" | ||
}, | ||
"integration:ditto": { | ||
"type": "the connection" | ||
} | ||
}, | ||
"resources": { | ||
"policy:/": { | ||
"grant": ["READ", "WRITE"], | ||
"revoke": [] | ||
}, | ||
"thing:/": { | ||
"grant": ["READ", "WRITE"], | ||
"revoke": [] | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
# HTTP requests made by the HTTP connection | ||
|
||
Before the HTTP connection publishes the thing-created event, it makes an access token request against the token | ||
endpoint to obtain a bearer token. | ||
|
||
``` | ||
POST /785e80cd-e6e6-452a-be97-a59c53edb4d9 HTTP/1.1 | ||
Host: webhook.site | ||
Accept: application/json | ||
Content-Type: application/x-www-form-urlencoded | ||
grant_type=client_credentials | ||
&client_id=my-client-id | ||
&client_secret=my-client-secret | ||
&scope=my-scope | ||
``` | ||
|
||
The request should appear at the access token webhook. The webhook should return the configured access token response. | ||
|
||
```json | ||
{ | ||
"access_token": "ewogICJhbGciOiAiUlMyNTYiLAogICJ0eXAiOiAiSldUIgp9.ewogICJhdWQiOiBbXSwKICAiY2xpZW50X2lkIjogIm15LWNsaWVudC1pZCIsCiAgImV4cCI6IDMyNTAzNjgwMDAwLAogICJleHQiOiB7fSwKICAiaWF0IjogMCwKICAiaXNzIjogImh0dHBzOi8vbG9jYWxob3N0LyIsCiAgImp0aSI6ICI3ODVlODBjZC1lNmU2LTQ1MmEtYmU5Ny1hNTljNTNlZGI0ZDkiLAogICJuYmYiOiAwLAogICJzY3AiOiBbCiAgICAibXktc2NvcGUiCiAgXSwKICAic3ViIjogIm15LXN1YmplY3QiCn0.QUJD", | ||
"expires_in": 1048576, | ||
"scope": "my-scope", | ||
"token_type": "bearer" | ||
} | ||
``` | ||
|
||
The HTTP connection will cache the access token and use it to authenticate itself at the event publishing webhook | ||
for each thing event, including the first thing-created event. | ||
|
||
``` | ||
POST /6148b899-736f-47e6-9382-90b1d721630e HTTP/1.1 | ||
Host: webhook.site | ||
Content-Type: application/vnd.eclipse.ditto+json | ||
Authorization: Bearer ewogICJhbGciOiAiUlMyNTYiLAogICJ0eXAiOiAiSldUIgp9.ewogICJhdWQiOiBbXSwKICAiY2xpZW50X2lkIjogIm15LWNsaWVudC1pZCIsCiAgImV4cCI6IDMyNTAzNjgwMDAwLAogICJleHQiOiB7fSwKICAiaWF0IjogMCwKICAiaXNzIjogImh0dHBzOi8vbG9jYWxob3N0LyIsCiAgImp0aSI6ICI3ODVlODBjZC1lNmU2LTQ1MmEtYmU5Ny1hNTljNTNlZGI0ZDkiLAogICJuYmYiOiAwLAogICJzY3AiOiBbCiAgICAibXktc2NvcGUiCiAgXSwKICAic3ViIjogIm15LXN1YmplY3QiCn0.QUJD | ||
{ | ||
"topic": "<namespace>/<name>/things/twin/events/created", | ||
"headers": {}, | ||
"path": "/", | ||
"value": { | ||
"policyId": "<policy-id>" | ||
}, | ||
"revision": 1 | ||
} | ||
``` | ||
|
||
The HTTP connection will obtain a new token from the access token webhook when the previous token is about to expire. | ||
|
||
Please [get in touch](feedback.html) if you have feedback or questions regarding this new functionality. | ||
<br/> | ||
<br/> | ||
{% include image.html file="ditto.svg" alt="Ditto" max-width=500 %} | ||
--<br/> | ||
The Eclipse Ditto team |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters