From 690a22cc6822747609637b646f201468d4dc5656 Mon Sep 17 00:00:00 2001
From: ralfbayer-db <69343965+ralfbayer-db@users.noreply.github.com>
Date: Wed, 5 Jun 2024 09:57:55 +0200
Subject: [PATCH 1/8] Update authentication.md
Intermediate commit, needs more work
---
spec/authentication.md | 62 +++++++++++++++++++++++++-----------------
1 file changed, 37 insertions(+), 25 deletions(-)
diff --git a/spec/authentication.md b/spec/authentication.md
index 7239b495..dc65c33c 100644
--- a/spec/authentication.md
+++ b/spec/authentication.md
@@ -9,26 +9,34 @@ permalink: /spec/authentication/
## Table of contents
1. [Introduction](#introduction)
-2. [Flow](#Flow)
-3. [JW identity token](#jw_identity_token)
-4. [Request for an access token](#request_for_access_token)
-5. [Validation of identity token](#validation_of_identity_token)
-6. [Configuration](#configuration)
-7. [Examples](#examples)
+2. [Using JWTs for Client Authentication](#JWTs)
+2.1 [Flow](#Flow)
+2.2 [JW identity token](#jw_identity_token)
+2.3 [Request for an access token](#request_for_access_token)
+2.4 [Validation of identity token](#validation_of_identity_token)
+2.5 [Configuration](#configuration)
+2.6 [Examples](#examples)
+3. [Using Client Credentials for Access Token Request](#client_credentials)
## Introduction
-The general requirement of the OSDM standard to use OAuth2 for authentication and authorization by means of JW tokens (JWTs) should be implemented in the following consistent manner between backends.
+The general requirement of the OSDM standard to use OAuth2 for authentication and authorization by means of JW tokens (JWTs) should be implemented in one of two consistent methods. Either by
+
+- **Using JWTs for Client Authentication** according to [RFC-7523 Section 2.2](https://datatracker.ietf.org/doc/html/rfc7523#section-2.2), or by
+- **Using Client Credentials for Access Token Request** according to [RFC.6749 Section 4.4.2](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4.2)
The following RFC documents apply:
1. [RFC-7519](https://datatracker.ietf.org/doc/rfc7519/) which explains what a JWT token is;
- 2. [RFC-6749$3.2](https://www.rfc-editor.org/rfc/rfc6749.html#section-3.2) which defines OAuth2 and the token endpoint involved in the creation of tokens;
- 3. [RFC-7521](https://datatracker.ietf.org/doc/rfc7521/) laying the groundwork for cryptographic client assertions;
- 4. [RFC-7523](https://datatracker.ietf.org/doc/rfc7523/) which describes how to properly secure the token endpoint with modern cryptography, thus not relying on static secrets;
- 5. [RFC-8725](https://datatracker.ietf.org/doc/rfc8725/) which gives guidance on securely validating and using JWTs.
+ 2. [RFC-6749 Section 3.2](https://datatracker.ietf.org/doc/html/rfc6749#section-3.2) which defines OAuth2 and the token endpoint involved in the creation of tokens;
+ 3. [RFC-6749 Section 4.4.2](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4.2) which defines the use of client credentials to obtain an access token;
+ 4. [RFC-7521](https://datatracker.ietf.org/doc/rfc7521/) laying the groundwork for cryptographic client assertions;
+ 5. [RFC-7523 Section 2.2](https://datatracker.ietf.org/doc/html/rfc7523#section-2.2) which describes how to properly secure the token endpoint with modern cryptography, thus not relying on static secrets;
+ 6. [RFC-8725](https://datatracker.ietf.org/doc/rfc8725/) which gives guidance on securely validating and using JWTs.
+
+This document defines the two variants of flows to be used. It also defines the parameters used, which must be agreed and exchanged bilaterally between the parties involved.
-This section defines the exact flow to be used. It also defines the parameters used, which must be agreed and exchanged bilaterally between the parties involved.
+## Using JWTs for Client Authentication
This flow uses a **client authentication assertion** in the form of a **JW identity token** (`private_key_jwt` in terms of OpenID Connect (OIDC)), which is cryptographically signed by the client (OSDM consumer) and can be verified by the server (OSDM provider).
@@ -36,7 +44,7 @@ The OSDM provider then issues a **JW access token** which can in turn be used by
This method makes it unnecessary to exchange actual client secrets between the consumers and providers of the service and relies on asymmetric cryptography, i.e., the use of private/public keys for signing such requests.
-## Flow
+### Flow
The general flow between consumer and provider looks like this:
@@ -48,7 +56,7 @@ In this flow, the following services are defined:
- The **OSDM Provider Login Service** is the service which controls the authentication of the OSDM Consumer by issuing JW access tokens
- The **OSDM Provider Functional Endpoints** implement the actual business logic of the OSDM Provider. Calls to these endpoints need to be authorized by providing the appropriate JW access tokens.
-## JW identity token
+### JW identity token
A JW token (JWT) consists of three parts which are separated by dots ("."):
@@ -60,7 +68,7 @@ Each part is separately encoded using Base64URL encoding. The encoded header and
Header and Payload of the JW token are encoded as JSON structures. Their content is defined in the following sections.
-### JW identity token header
+#### JW identity token header
The identity token contains the following header fields. Where some fields are optional according to the relevant RFC, we still consider them mandatory for the purposes of usage within the OSDM standard.
@@ -70,7 +78,7 @@ The identity token contains the following header fields. Where some fields are o
| kid | OPTIONAL | MANDATORY | Id of the key used for signing this token | defined by OSDM consumer. Should be provided to the OSDM provider. |
| typ | REQUIRED | MANDATORY | Type of the token | fixed value "JWT" |
-### JW identity token payload
+#### JW identity token payload
| Attribute | RFC requirement | OSDM requirement | Description | Recommended value |
| --------- | --------------- | ---------------- | ----------------------------------------- | ------------------------------------------------------------------ |
@@ -85,11 +93,11 @@ The identity token contains the following header fields. Where some fields are o
Note: All timestamps are in "Unix epoch", which is defined as the number of seconds since 1st January, 1970 UTC.
-### JW identity token signature
+#### JW identity token signature
The signature is obtained by creating the string `.`, and signing this string with the private key of the OSDM consumer using the algorithm specified in the JWT header field "alg". The signature is then also Base64URL encoded and added to the token.
-## Request for an access token
+### Request for an access token
To obtain the actual JW access token required to authenticate the functional OSDM requests, a token request message needs to be issued to the OSDM provider's login service. This has the following attributes:
@@ -103,7 +111,7 @@ The provider should set the **expires_in** attribute of the response, so that th
Consumers should cache the access token in order to avoid overloading the provider's login server, using the value from the expires_in attribute to invalidate cache entries.
-## Validation of identity token
+### Validation of identity token
The provider's login service should take certain steps in order to validate the identity token received from the consumer before it issues the access token. The most important include:
@@ -121,7 +129,7 @@ The provider's login service should take certain steps in order to validate the
Additionally, implementers should consult [RFC 8725](https://datatracker.ietf.org/doc/html/rfc8725) for guidance on securely validating and using JWTs, both in the login service and in the functional endpoints.
-## Configuration
+### Configuration
Some configuration parameters need to be agreed upon bilaterally between the partners. They are listed in the following table.
@@ -136,13 +144,13 @@ Some configuration parameters need to be agreed upon bilaterally between the par
When the signing key pair is **rotated** (which should happen on a regular basis), the consumer needs to provide the new signing key ID and the new public key to the provider.
-## Examples
+### Examples
Some examples are provided here.
-### Example JW identity token
+#### Example JW identity token
-#### JSON structure
+##### JSON structure
**Header:**
```
@@ -167,11 +175,11 @@ Some examples are provided here.
}
```
-#### Encoded token
+##### Encoded token
`eyJhbGciPSJSUzI1NiIsInR5cCI9IkpXVCIsImtpZCI9IjEyMzQ1Njc4OTAifQ.eyJpc3MiPSJodHRwczovL3d3dy5iYWhuLmRlIiwic3ViIj0iVUlDX09TRE1fMTA4MF80IiwiYXVkIj0iaHR0cHM6Ly9vc2RtLXByb3ZpZGVyLmV1L2xvZ29uLXNlcnZlci9wdWJsaWMvdG9rZW4iLCJleHAiPSIxNzA5MDQxMzEyIiwic2NvcGUiPSJ1aWNfb3NkbSIsIm5iZiI9IjE3MDkwNDAyOTIiLCJpYXQiPSIxNzA5MDQwNDEyIiwiaXRpIj0iZTU3YjA5MDEtMTljZi00NzFlLTgxZmUtNjFiNmE3ZWUxOWI3In0.`
-### Example request
+#### Example request
```
POST /logon-server/public/token
@@ -181,3 +189,7 @@ grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&assertion=eyJhbGciPSJSUzI1NiIsInR5cCI9IkpXVCIsImtpZCI9IjEyMzQ1Njc4OTAifQ.eyJpc3MiPSJo...
&scope=uic_osdm
```
+
+## Using Client Credentials for Access Token Request
+
+TODO
From fe81320df8bf4b5bf34d5244859a48ec7d614dc1 Mon Sep 17 00:00:00 2001
From: ralfbayer-db <69343965+ralfbayer-db@users.noreply.github.com>
Date: Wed, 5 Jun 2024 10:47:41 +0200
Subject: [PATCH 2/8] Update authentication.md
Updated Section 2 to change from RFC 7523 2.1 to 2.2
---
spec/authentication.md | 28 +++++++++++++++-------------
1 file changed, 15 insertions(+), 13 deletions(-)
diff --git a/spec/authentication.md b/spec/authentication.md
index dc65c33c..0a0a8097 100644
--- a/spec/authentication.md
+++ b/spec/authentication.md
@@ -22,7 +22,7 @@ permalink: /spec/authentication/
The general requirement of the OSDM standard to use OAuth2 for authentication and authorization by means of JW tokens (JWTs) should be implemented in one of two consistent methods. Either by
-- **Using JWTs for Client Authentication** according to [RFC-7523 Section 2.2](https://datatracker.ietf.org/doc/html/rfc7523#section-2.2), or by
+- **Using JWTs for Client Authentication** according to [RFC-7523 Section 2.2](https://datatracker.ietf.org/doc/html/rfc7523#section-2.2), (recommended) or by
- **Using Client Credentials for Access Token Request** according to [RFC.6749 Section 4.4.2](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4.2)
The following RFC documents apply:
@@ -38,7 +38,7 @@ This document defines the two variants of flows to be used. It also defines the
## Using JWTs for Client Authentication
-This flow uses a **client authentication assertion** in the form of a **JW identity token** (`private_key_jwt` in terms of OpenID Connect (OIDC)), which is cryptographically signed by the client (OSDM consumer) and can be verified by the server (OSDM provider).
+This flow uses a **client authentication assertion** in the form of a **JW identity token** (`private_key_jwt` in terms of OpenID Connect (OIDC)), which is cryptographically signed by the client (OSDM consumer) and can be verified by the server (OSDM provider). It is the recommended
The OSDM provider then issues a **JW access token** which can in turn be used by the OSDM consumer to prove their access rights to the OSDM endpoints by providing the JW access token in the Authenticate header of the OSDM endpoint invocation.
@@ -82,8 +82,8 @@ The identity token contains the following header fields. Where some fields are o
| Attribute | RFC requirement | OSDM requirement | Description | Recommended value |
| --------- | --------------- | ---------------- | ----------------------------------------- | ------------------------------------------------------------------ |
-| iss | REQUIRED | MANDATORY | Issuer of the identity token | defined by the OSDM consumer, use the URL of the public website. |
-| sub | REQUIRED | MANDATORY | "Username" of the client | defined by the OSDM provider |
+| iss | REQUIRED | MANDATORY | Issuer of the identity token | defined by the OSDM provider (client id) |
+| sub | REQUIRED | MANDATORY | Identity of the client | defined by the OSDM provider (client id) |
| aud | REQUIRED | MANDATORY | URL login service endpoint | defined by the OSDM provider |
| exp | REQUIRED | MANDATORY | Timestamp when this request expires | current time + grace period of at least 2 minutes (120 seconds) |
| scope | OPTIONAL | MANDATORY | Usage of the token | fixed value "uic_osdm" |
@@ -101,8 +101,9 @@ The signature is obtained by creating the string `
To obtain the actual JW access token required to authenticate the functional OSDM requests, a token request message needs to be issued to the OSDM provider's login service. This has the following attributes:
-- `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer`
-- `assertion=`
+- `grant_type=client_credentials
+- `client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer`
+- `client_assertion=`
- `scope=uic_osdm`
The `` means the JW identity token which has been described above.
@@ -118,7 +119,7 @@ The provider's login service should take certain steps in order to validate the
- Check the signing algorithm against a whitelist of allowed algorithms or against a pinned value for the consumer, to mitigate these attacks:
- A third party (attacker) sends an identity token with alg: none, disabling signature verification and allowing token forgery.
- A third party (attacker) sends an identity token with alg set to a symmetric algorithm, but kid set to an asymmetric key. This allows token forgery if the public key is known
-- Check that sub, kid and the public key match (note: the provider should store this triplet), to mitigate this attack:
+- Check that sub, iss, kid and the public key match (note: the provider should store this data), to mitigate this attack:
- Consumer signs an identity token for another consumer's username with their own key
- Check the signature of the token against the public key of the requestor, to mitigate this attack:
- A third party (attacker) tries to request an access token without knowledge of the secret key, but tries anyway
@@ -137,8 +138,8 @@ Some configuration parameters need to be agreed upon bilaterally between the par
| ------------------------------------ | ----------------------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------ |
| Signing algorithm | JW identity token, header field 'alg' | Algorithm used for signing the JW identity token | Mutually agreed between provider and consumer |
| Signing key ID | JW identity token, header field 'kid' | Key used for signing the JW identity token | Defined by consumer |
-| Issuer of JW identity token | JW identity token, payload field 'iss' | Identifies the issuer of the JW identity token | Defined by consumer, usually the URL of their public website |
-| Subject of the access token request | JW identity token, payload field 'sub' | "Username" of the client within the provider's system | Defined by provider |
+| Issuer of JW identity token | JW identity token, payload field 'iss' | Identity of the client within the provider's system | Defined by provider |
+| Subject of the access token request | JW identity token, payload field 'sub' | Identity of the client within the provider's system | Defined by provider |
| Audience of the access token request | JW identity token, payload field 'aud' | URL of the login service | Defined by provider |
| Public key | Validation of signature within the provider's login service | Public key used for validating signature of identity token | Defined by consumer, part of the public/private key pair |
@@ -164,7 +165,7 @@ Some examples are provided here.
**Payload:**
```
{
- "iss" = "https://osdm-consumer.eu/",
+ "iss" = "UIC_OSDM_1080_4",
"sub" = "UIC_OSDM_1080_4",
"aud" = "https://osdm-provider.eu/logon-server/public/token",
"exp" = "1709041312",
@@ -177,7 +178,7 @@ Some examples are provided here.
##### Encoded token
-`eyJhbGciPSJSUzI1NiIsInR5cCI9IkpXVCIsImtpZCI9IjEyMzQ1Njc4OTAifQ.eyJpc3MiPSJodHRwczovL3d3dy5iYWhuLmRlIiwic3ViIj0iVUlDX09TRE1fMTA4MF80IiwiYXVkIj0iaHR0cHM6Ly9vc2RtLXByb3ZpZGVyLmV1L2xvZ29uLXNlcnZlci9wdWJsaWMvdG9rZW4iLCJleHAiPSIxNzA5MDQxMzEyIiwic2NvcGUiPSJ1aWNfb3NkbSIsIm5iZiI9IjE3MDkwNDAyOTIiLCJpYXQiPSIxNzA5MDQwNDEyIiwiaXRpIj0iZTU3YjA5MDEtMTljZi00NzFlLTgxZmUtNjFiNmE3ZWUxOWI3In0.`
+`eyJhbGciPSJSUzI1NiIsInR5cCI9IkpXVCIsImtpZCI9IjEyMzQ1Njc4OTAifQ.eyJpc3MiPSJVSUNfT1NETV8xMDgwXzQiLCJzdWIiPSJVSUNfT1NETV8xMDgwXzQiLCJhdWQiPSJodHRwczovL29zZG0tcHJvdmlkZXIuZXUvbG9nb24tc2VydmVyL3B1YmxpYy90b2tlbiIsImV4cCI9IjE3MDkwNDEzMTIiLCJzY29wZSI9InVpY19vc2RtIiwibmJmIj0iMTcwOTA0MDI5MiIsImlhdCI9IjE3MDkwNDA0MTIiLCJpdGkiPSJlNTdiMDkwMS0xOWNmLTQ3MWUtODFmZS02MWI2YTdlZTE5YjcifQo.`
#### Example request
@@ -185,8 +186,9 @@ Some examples are provided here.
POST /logon-server/public/token
Host: osdm-provider.eu
Content-Type: application/x-www-form-urlencoded
-grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
-&assertion=eyJhbGciPSJSUzI1NiIsInR5cCI9IkpXVCIsImtpZCI9IjEyMzQ1Njc4OTAifQ.eyJpc3MiPSJo...
+grant_type=client_credentials
+&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
+&client_assertion=eyJhbGciPSJSUzI1NiIsInR5cCI9IkpXVCIsImtpZCI9IjEyMzQ1Njc4OTAifQ.eyJpc3MiPSJVSUNf...
&scope=uic_osdm
```
From 7584a770a8019605ab34c14e9e05ebc143996058 Mon Sep 17 00:00:00 2001
From: ralfbayer-db <69343965+ralfbayer-db@users.noreply.github.com>
Date: Wed, 5 Jun 2024 11:17:51 +0200
Subject: [PATCH 3/8] Add files via upload
---
.../authentication_flow_client_secret.png | Bin 0 -> 28281 bytes
.../authentication_flow_client_secret.puml | 11 +++++++++++
2 files changed, 11 insertions(+)
create mode 100644 images/authentication/authentication_flow_client_secret.png
create mode 100644 images/authentication/authentication_flow_client_secret.puml
diff --git a/images/authentication/authentication_flow_client_secret.png b/images/authentication/authentication_flow_client_secret.png
new file mode 100644
index 0000000000000000000000000000000000000000..c584a9ce067d03897347f4e987e9b506a88b62a3
GIT binary patch
literal 28281
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
literal 0
HcmV?d00001
diff --git a/images/authentication/authentication_flow_client_secret.puml b/images/authentication/authentication_flow_client_secret.puml
new file mode 100644
index 00000000..f2e41eff
--- /dev/null
+++ b/images/authentication/authentication_flow_client_secret.puml
@@ -0,0 +1,11 @@
+@startuml Client Secret Token Generation Flow
+OSDM_Consumer -> OSDM_Provider_Login_Service : Request for Access Token including Client Credentials
+OSDM_Provider_Login_Service -> OSDM_Provider_Login_Service : Validate Client Credentials
+OSDM_Provider_Login_Service -> OSDM_Provider_Login_Service : Create and Sign Access Token
+OSDM_Provider_Login_Service -> OSDM_Consumer : JW Access Token
+OSDM_Consumer -> OSDM_Consumer : Cache JW Access Token
+OSDM_Consumer -> OSDM_Provider_Functional_Endpoints : OSDM Call e.g. POST /offers including Access Tolken
+OSDM_Provider_Functional_Endpoints -> OSDM_Provider_Functional_Endpoints : Validate JW Access Token
+OSDM_Provider_Functional_Endpoints -> OSDM_Provider_Functional_Endpoints : Process OSDM functional call
+OSDM_Provider_Functional_Endpoints -> OSDM_Consumer : OSDM Response e.g. OfferResponse
+@enduml
\ No newline at end of file
From cf66a9c24c1ea9824d04f9a137fe9acc9afec524 Mon Sep 17 00:00:00 2001
From: ralfbayer-db <69343965+ralfbayer-db@users.noreply.github.com>
Date: Wed, 5 Jun 2024 11:47:55 +0200
Subject: [PATCH 4/8] Update authentication.md
Added section for client authentication using client secret, cleaned up TOC so allow for both methods.
---
spec/authentication.md | 84 +++++++++++++++++++++++++++++++++++-------
1 file changed, 71 insertions(+), 13 deletions(-)
diff --git a/spec/authentication.md b/spec/authentication.md
index 0a0a8097..f8368a64 100644
--- a/spec/authentication.md
+++ b/spec/authentication.md
@@ -9,14 +9,19 @@ permalink: /spec/authentication/
## Table of contents
1. [Introduction](#introduction)
-2. [Using JWTs for Client Authentication](#JWTs)
-2.1 [Flow](#Flow)
+2. [Using JWTs for client authentication](#JWTs)
+2.1 [Flow using JWTs](#flow_jwt)
2.2 [JW identity token](#jw_identity_token)
-2.3 [Request for an access token](#request_for_access_token)
+2.3 [Request for an access token using JWTs](#request_for_access_token_jwt)
2.4 [Validation of identity token](#validation_of_identity_token)
-2.5 [Configuration](#configuration)
+2.5 [Configuration for identity JWTs](#configuration_jwt)
2.6 [Examples](#examples)
-3. [Using Client Credentials for Access Token Request](#client_credentials)
+3. [Using client credentials for access token request](#client_credentials)
+3.1 [Flow using client credentials](#flow_client_credentials)
+3.2 [Request for an access token using a client secret](#request_for_access_token_with_client_credentials)
+3.3 [Validation of client credentials](#validation_of_client_credentials)
+3.4 [Configuration of client credentials](#configuration_client_credentials)
+3.5 [Example request using client credentials](#example_request_client_credentials)
## Introduction
@@ -36,7 +41,7 @@ The following RFC documents apply:
This document defines the two variants of flows to be used. It also defines the parameters used, which must be agreed and exchanged bilaterally between the parties involved.
-## Using JWTs for Client Authentication
+## Using JWTs for alient authentication
This flow uses a **client authentication assertion** in the form of a **JW identity token** (`private_key_jwt` in terms of OpenID Connect (OIDC)), which is cryptographically signed by the client (OSDM consumer) and can be verified by the server (OSDM provider). It is the recommended
@@ -44,7 +49,7 @@ The OSDM provider then issues a **JW access token** which can in turn be used by
This method makes it unnecessary to exchange actual client secrets between the consumers and providers of the service and relies on asymmetric cryptography, i.e., the use of private/public keys for signing such requests.
-### Flow
+### Flow using JWTs
The general flow between consumer and provider looks like this:
@@ -97,11 +102,11 @@ Note: All timestamps are in "Unix epoch", which is defined as the number of seco
The signature is obtained by creating the string `.`, and signing this string with the private key of the OSDM consumer using the algorithm specified in the JWT header field "alg". The signature is then also Base64URL encoded and added to the token.
-### Request for an access token
+### Request for an access token using JWTs
To obtain the actual JW access token required to authenticate the functional OSDM requests, a token request message needs to be issued to the OSDM provider's login service. This has the following attributes:
-- `grant_type=client_credentials
+- `grant_type=client_credentials`
- `client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer`
- `client_assertion=`
- `scope=uic_osdm`
@@ -130,7 +135,7 @@ The provider's login service should take certain steps in order to validate the
Additionally, implementers should consult [RFC 8725](https://datatracker.ietf.org/doc/html/rfc8725) for guidance on securely validating and using JWTs, both in the login service and in the functional endpoints.
-### Configuration
+### Configuration for identity JWTs
Some configuration parameters need to be agreed upon bilaterally between the partners. They are listed in the following table.
@@ -143,7 +148,8 @@ Some configuration parameters need to be agreed upon bilaterally between the par
| Audience of the access token request | JW identity token, payload field 'aud' | URL of the login service | Defined by provider |
| Public key | Validation of signature within the provider's login service | Public key used for validating signature of identity token | Defined by consumer, part of the public/private key pair |
-When the signing key pair is **rotated** (which should happen on a regular basis), the consumer needs to provide the new signing key ID and the new public key to the provider.
+When the signing key pair is **rotated** (which should happen on a regular basis), the consumer needs to provide the new signing key ID and the new public key to the provider, and the provider
+should - for a limited time - accept either pair of key_ID/public_key for validation.
### Examples
@@ -192,6 +198,58 @@ grant_type=client_credentials
&scope=uic_osdm
```
-## Using Client Credentials for Access Token Request
+## Using client credentials for access token request
-TODO
+As an alternative to the flow described above using a signed JWT as the client credential, a simpler method can - at the discretion of the OSDM provider, and subject to mutual agreement with their
+consumers - also be used. The resulting JWT access tokens are identical to those obtained by the other method, so there is no difference for the subsequent invocations of functional OSDM endpoints.
+
+This method - according to [RFC-6749 Section 4.4.2](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4.2) - relies on the exchange of a client_id and a client_secret between the OSDM provider
+and the OSDM consumer.
+
+### Flow using client credentials
+
+The flow to be implemented by the OSDM consumer is somewhat simpler to the other method.
+
+![Authentication_flow](../images/authentication/authentication_flow_client_secret.png)
+
+### Request for an access token using a client secret
+
+To obtain the actual JW access token required to authenticate the functional OSDM requests, a token request message needs to be issued to the OSDM provider's login service. This has the following attributes:
+
+- `grant_type=client_credentials`
+- `client_id=`
+- `client_secret=`
+- `scope=uic_osdm`
+
+The provider should set the **expires_in** attribute of the response, so that the consumer does not need to parse the token content.
+
+Consumers should cache the access token in order to avoid overloading the provider's login server, using the value from the expires_in attribute to invalidate cache entries.
+
+### Validation of client credentials
+
+The login service needs to validate that client_id and client_secret match. To provide additional security, it may also include other methods, like checking the IP address of the requestor against a defined
+range of allowed IP addresses.
+
+### Configuration of client credentials
+
+Some configuration parameters need to be agreed upon bilaterally between the partners. They are listed in the following table.
+
+| Parameter | Usage | Explanation | Parameter flow |
+| ---------------| ---------------------------------------------| ------------------------------------------------------------- | ---------------------------|
+| Client id | Token request, parameter 'client_id' | Identity of the client within the provider's system | Defined by provider |
+| Client secret | Token request, parameter 'client_secret' | Secet ('password') of the client within the provider's system | Defined by provider |
+
+When credentials need to be **rotated** (which should happen on a regular basis), the provider needs to provide the consumer with a **second** client_id/client_secret pair and needs,
+for a limited time, to accept either for validation. When the consumer has switched to the new client_id/client_secret pair, the original pair should be disabled in the provider's system.
+
+### Example request using client credentials
+
+```
+POST /logon-server/public/token
+Host: osdm-provider.eu
+Content-Type: application/x-www-form-urlencoded
+grant_type=client_credentials
+&client_id=UIC_OSDM_1080_4
+&client_secret=MXCleFO22w2yAfolea75lrIE5RdqimPL
+&scope=uic_osdm
+```
From c6641393430da1ff578d1f9cea3e0c0d6ad78f59 Mon Sep 17 00:00:00 2001
From: ralfbayer-db <69343965+ralfbayer-db@users.noreply.github.com>
Date: Wed, 5 Jun 2024 11:48:37 +0200
Subject: [PATCH 5/8] Update authentication.md
Typo
---
spec/authentication.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/spec/authentication.md b/spec/authentication.md
index f8368a64..43995aef 100644
--- a/spec/authentication.md
+++ b/spec/authentication.md
@@ -41,7 +41,7 @@ The following RFC documents apply:
This document defines the two variants of flows to be used. It also defines the parameters used, which must be agreed and exchanged bilaterally between the parties involved.
-## Using JWTs for alient authentication
+## Using JWTs for client authentication
This flow uses a **client authentication assertion** in the form of a **JW identity token** (`private_key_jwt` in terms of OpenID Connect (OIDC)), which is cryptographically signed by the client (OSDM consumer) and can be verified by the server (OSDM provider). It is the recommended
From 6a4588d9b3d7d2779668db20263a552af5ab2d18 Mon Sep 17 00:00:00 2001
From: ralfbayer-db <69343965+ralfbayer-db@users.noreply.github.com>
Date: Wed, 5 Jun 2024 11:49:52 +0200
Subject: [PATCH 6/8] Update authentication.md
---
spec/authentication.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/spec/authentication.md b/spec/authentication.md
index 43995aef..7d404bc3 100644
--- a/spec/authentication.md
+++ b/spec/authentication.md
@@ -9,7 +9,7 @@ permalink: /spec/authentication/
## Table of contents
1. [Introduction](#introduction)
-2. [Using JWTs for client authentication](#JWTs)
+2. [Using JWTs for client authentication](#jwts)
2.1 [Flow using JWTs](#flow_jwt)
2.2 [JW identity token](#jw_identity_token)
2.3 [Request for an access token using JWTs](#request_for_access_token_jwt)
@@ -41,7 +41,7 @@ The following RFC documents apply:
This document defines the two variants of flows to be used. It also defines the parameters used, which must be agreed and exchanged bilaterally between the parties involved.
-## Using JWTs for client authentication
+## Using JWTs for client authentication
This flow uses a **client authentication assertion** in the form of a **JW identity token** (`private_key_jwt` in terms of OpenID Connect (OIDC)), which is cryptographically signed by the client (OSDM consumer) and can be verified by the server (OSDM provider). It is the recommended
From a8acd893d4e588ab0d0a1c76f62e0b34e8fb1859 Mon Sep 17 00:00:00 2001
From: ralfbayer-db <69343965+ralfbayer-db@users.noreply.github.com>
Date: Wed, 5 Jun 2024 12:02:10 +0200
Subject: [PATCH 7/8] Update authentication.md
Added notes on access tokens to address #436
---
spec/authentication.md | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/spec/authentication.md b/spec/authentication.md
index 7d404bc3..1011cd20 100644
--- a/spec/authentication.md
+++ b/spec/authentication.md
@@ -22,6 +22,7 @@ permalink: /spec/authentication/
3.3 [Validation of client credentials](#validation_of_client_credentials)
3.4 [Configuration of client credentials](#configuration_client_credentials)
3.5 [Example request using client credentials](#example_request_client_credentials)
+4 [Notes on access tokens](#notes_on_access_tokens)
## Introduction
@@ -253,3 +254,12 @@ grant_type=client_credentials
&client_secret=MXCleFO22w2yAfolea75lrIE5RdqimPL
&scope=uic_osdm
```
+## Notes on access tokens
+
+The access tokens provided by the login service behave according to the relevant standards, particularly:
+
+- with the backend-to-backend flows described in this document, the login service only provides an **access token** - no refresh tokens are provided
+- the expiry time of the access token is provided in the **expires_in** element of the response
+- there is no need for the consumer to parse the access token
+- a consumer may request multiple access tokens in parallel requests (e.g. from multiple instances of their services), in particular:
+- requesting a new access token does **not** invalidate a previously issued access token for the same set of credentials
From bf8a8751e5c0cd633f9f5c61c50c42b2fb050b10 Mon Sep 17 00:00:00 2001
From: ralfbayer-db <69343965+ralfbayer-db@users.noreply.github.com>
Date: Wed, 5 Jun 2024 12:02:55 +0200
Subject: [PATCH 8/8] Update authentication.md
Fixed TOC
---
spec/authentication.md | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/spec/authentication.md b/spec/authentication.md
index 1011cd20..887fc797 100644
--- a/spec/authentication.md
+++ b/spec/authentication.md
@@ -10,19 +10,19 @@ permalink: /spec/authentication/
1. [Introduction](#introduction)
2. [Using JWTs for client authentication](#jwts)
-2.1 [Flow using JWTs](#flow_jwt)
-2.2 [JW identity token](#jw_identity_token)
-2.3 [Request for an access token using JWTs](#request_for_access_token_jwt)
-2.4 [Validation of identity token](#validation_of_identity_token)
-2.5 [Configuration for identity JWTs](#configuration_jwt)
-2.6 [Examples](#examples)
+2.1. [Flow using JWTs](#flow_jwt)
+2.2. [JW identity token](#jw_identity_token)
+2.3. [Request for an access token using JWTs](#request_for_access_token_jwt)
+2.4. [Validation of identity token](#validation_of_identity_token)
+2.5. [Configuration for identity JWTs](#configuration_jwt)
+2.6. [Examples](#examples)
3. [Using client credentials for access token request](#client_credentials)
-3.1 [Flow using client credentials](#flow_client_credentials)
-3.2 [Request for an access token using a client secret](#request_for_access_token_with_client_credentials)
-3.3 [Validation of client credentials](#validation_of_client_credentials)
-3.4 [Configuration of client credentials](#configuration_client_credentials)
-3.5 [Example request using client credentials](#example_request_client_credentials)
-4 [Notes on access tokens](#notes_on_access_tokens)
+3.1. [Flow using client credentials](#flow_client_credentials)
+3.2. [Request for an access token using a client secret](#request_for_access_token_with_client_credentials)
+3.3. [Validation of client credentials](#validation_of_client_credentials)
+3.4. [Configuration of client credentials](#configuration_client_credentials)
+3.5. [Example request using client credentials](#example_request_client_credentials)
+4. [Notes on access tokens](#notes_on_access_tokens)
## Introduction