An OAuth 2.0 Device Code flow for DomoticzLinky, a Domoticz plugin, adapted to Enedis data hub.
This project if a fork of https://github.com/aaronpk/Device-Flow-Proxy-Server, a big thanks to him.
This service acts as an OAuth server that implements the device code flow, proxying to a real OAuth server behind the scenes.
Compared to the original project, this implementation uses MongoDB instead of Redis (because it was easier for me to find a serious and free provider with MongoDB), it sends back all parameters received during redirect (mainly to get usage_point_id from Enedis), it adds a feature to provide client_secret from .env file instead of getting it from device request, to keep it private, and it can act as a proxy to add this client_secret from .env
file to Enedis when refreshing tokens, if you don't want the device to provide it.
cp .env.example .env
composer install
In the .env
file, fill out the required variables and don't forget to change APP_SECRET to another random string.
You will need to install MongoDB if it is not already on your system, or point to an existing MongoDB server in the config file.
Define your OAuth server's authorization endpoint and token endpoint URL, and optionaly the client_secret, this way it will be kept private between your web server and Enedis, otherwise the device must provide it during requests.
The device will need to register an application at the OAuth server to get a client ID. You'll need to set the proxy's URL as the callback URL in the OAuth application registration:
http://localhost:8080/auth/redirect
The device can begin the flow by making a POST request to this proxy:
curl http://localhost:8080/device/code -d client_id=1234567890
or if your device must provide client_secret (otherwise you can specify it in the .env
file)
curl http://localhost:8080/device/code -d client_id=1234567890 -d client_secret=12345678-1234-1234-1234-1234567890ab
The response will contain the URL the user should visit and the code they should enter, as well as a long device code.
{
"device_code": "5cb3a6029c967a7b04f642a5b92b5cca237ec19d41853f55dcce98a4d2aa528f",
"user_code": "248707",
"verification_uri": "http://localhost:8080/device",
"expires_in": 300,
"interval": 5
}
The device should instruct the user to visit the URL and enter the code, or can provide a full link that pre-fills the code for the user in case the device is displaying a QR code.
http://localhost:8080/device?code=248707
If the servers are using the client_credentials flow, add a state parameter ending with "-cg"
http://localhost:8080/device?code=248707&state=-cg
The device should then poll the token endpoint at the interval provided, making a POST request like the below:
curl http://localhost:8080/device/token -d grant_type=urn:ietf:params:oauth:grant-type:device_code \
-d client_id=1234567890 \
-d device_code=5cb3a6029c967a7b04f642a5b92b5cca237ec19d41853f55dcce98a4d2aa528f
While the user is busy logging in, the response will be
{"error":"authorization_pending"}
Once the user has finished logging in and granting access to the application, the response will contain an access token.
{
"access_token": "8YQZTbKML5Ntx2iuzdBJTvWE4XzIlSHeYmu4Y1GVpjrft2q768wavr",
"refresh_token": "QcMhancv1wPyi8uwnkzcTNyd397oC7K0La8otPcssYMpXT",
"token_type": "Bearer",
"expires_in": 12600,
"usage_point_id" : "1234567890abcd"
}
If the client_secret is not known by the device but is configured in the .env
file, you can refresh the token with:
curl http://localhost:8080/device/proxy -d grant_type=refresh_token \
-d client_id=1234567890 \
-d refresh_token=QcMhancv1wPyi8uwnkzcTNyd397oC7K0La8otPcssYMpXT
or if the servers are using the client_credentials flow:
curl http://localhost:8080/device/token -d grant_type=refresh_token \
-d client_id=1234567890 \
-d usages_point_id=1234567890abcd \
-d refresh_token=QcMhancv1wPyi8uwnkzcTNyd397oC7K0La8otPcssYMpXT
You'll get a response with new access and refresh tokens.
{
"access_token": "6czyedyLUHvyjtWZuWwBLkXNZhzk9QLP9Cip5NPhFNmc8znWoPipnW",
"refresh_token": "YpcX7v7sohTvDTWfzZpj4DyfZgvYtJKdNj7YHEhr3ZH7FCiqSDCDJ2",
"token_type": "Bearer",
"expires_in": 12600,
"usage_point_id" : "1234567890abcd"
}
If the servers are not using the client_credentials flow, you can now send your data request to final server with the obtained access_token.
If the servers are using the client_credentials flow, you can now send your data request to this address (replace path1/path2 with the path you want your request to go to, the server address is configured with DATA_ENDPOINT variable in .env file):
curl --header "Authorization: Bearer 6czyedyLUHvyjtWZuWwBLkXNZhzk9QLP9Cip5NPhFNmc8znWoPipnW" \
"http://localhost:8080/data/proxy/path1/path2?usage_point_id=1234567890abcd¶m1=value¶m2=value"