Skip to content
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

Claims to be used in oAuth Access tokens/ OIDC Identity tokens #100

Closed
Elisabeth-Ericsson opened this issue Dec 20, 2023 · 21 comments · Fixed by #121
Closed

Claims to be used in oAuth Access tokens/ OIDC Identity tokens #100

Elisabeth-Ericsson opened this issue Dec 20, 2023 · 21 comments · Fixed by #121
Labels
documentation Improvements or additions to documentation get fixed by PR #121 v0.2-candidate Custom label for issues that are candidates to be fixed in v0.2 (if needed)

Comments

@Elisabeth-Ericsson
Copy link
Contributor

Elisabeth-Ericsson commented Dec 20, 2023

Problem description
There have been several discussion rounds on the format of the scope parameter in the access token request. The agreement for the short term solution describes a proprietary format and has been included in
(1) CAMARA-API-access-and-user-consent-management.md (IdentityAndConsentManagement/documentation/CAMARA-API-access-and-user-consent.md at main · camaraproject/IdentityAndConsentManagement (github.com)).
Another document describes the generic handling of access tokens. This is
(2) CAMARA-AuthN-AuthZ-Concept.md document (https://github.com/camaraproject/IdentityAndConsentManagement/blob/main/documentation/CAMARA-AuthN-AuthZ-Concept.md ).

Here it is stated that: Access tokens should be signed and encoded as JWT (Json Web Token), with the bearer type specified.
In the very same document it is stated that the resulting token must be checked (and therefore contain claims) on:
• Expiry of the token (exp_claim)
• Issuer recognized as a trusted issuer (iss_claim)
• Consumer whitelisted in ACL list (azp_claim)

However, the format of scope claim as consequence of description in (1) for access and consent management and the handling of purpose are not documented. This needs to be agreed urgently, since the service implementations usually evaluate the content of the access token and therefore must know what to expect.

Even though the authorization request uses a custom format for the scope parameter (and includes the purpose in it), this is not allowed in the resulting access token. According to the standard RFC 8693: OAuth 2.0 Token Exchange (rfc-editor.org), 4.2, the value of the scope claim is a JSON string containing a space-separated list of scopes associated with the token, in the format described in Section 3.3 of [RFC6749].
From RFC 6749: The value of the scope parameter is expressed as a list of space-
delimited, case-sensitive strings. The strings are defined by the
authorization server. If the value contains multiple space-delimited
strings, their order does not matter, and each string adds an
additional access range to the requested scope.

Expected action
Thus the scope claim in the access token must have the exact same values as the scope strings defined in the API definition registered with the authorization server.
In case the API name as “bundle scope” is used in the scope request, this bundle scope must be extended to the individual list of scopes as contained in the API definition.

The purpose parameter should be included in the access token as well, but cannot be conveyed in the scope claim of the access token without breaking the standard RFC 8693. Thus the purpose parameter should be included in the access token in a separate claim.
Our proposal is to use “crn” claim describing the call reason. This is a public claim specified by JSON Web Token (JWT) (iana.org). It has been introduced with RFC-ietf-stir-passport-rcd-26 (draft-ietf-stir-passport-rcd-26 - PASSporT Extension for Rich Call Data).
Alternatively, a new claim, e.g. called “purpose” could be requested.

Additional context and example
An example for an access token generated on an authorize request for SIM-SwaP API would then look like:

GET /authorize?response_type=code&client_id=myApp&client_secret=0xahh23457& scope=“dpv:fraud-prevention#check-sim-swap” & redirect_url=aggregator_callback

{
"iss": https://mycsp.auth0.com/,
"sub": "+46772334567",
"aud": " https://mycsp.com/APIexposureService ",
"exp": 1490922820,
"iat": 1490886820,
“client_id”:0x3445a2,
“azp”:”coolApp”,
"scope": " check-sim-swap ",
"crn": " dpv:fraud-prevention "
}

When the request contains the API name instead of a single technical scope (as exemplified below), then the generated access token must contain the list of data scopes provided by the API.

GET /authorize?response_type=code&client_id=myApp&client_secret=0xahh23457& scope=“dpv:fraud-prevention#sim-swap” & redirect_url=aggregator_callback

{
"iss": https://mycsp.auth0.com/,
"sub": "+46772334567",
"aud": " https://mycsp.com/APIexposureService ",
"exp": 1490922820,
"iat": 1490886820,
“client_id”:0x3445a2,
“azp”:”coolApp”,
"scope": "check-sim-swap retrieve-sim-swap-date",
"crn": " dpv:fraud-prevention "
}

Additional context

@Elisabeth-Ericsson Elisabeth-Ericsson added the documentation Improvements or additions to documentation label Dec 20, 2023
@DT-DawidWroblewski
Copy link
Contributor

Hi @Elisabeth-Ericsson !

Thanks for this issue - I have an impression that we're missing a significant piece of flow in Camara, which is Access Token issuance.

Knowing that Camara follows the OpenID Connect framework, we should rather discuss ID Tokens, as JWTs that handle claims, NOT Access Tokens, as these point towards using OAUTH2.0 inside Camara, which is not true.

Therefore, Camara has to be very precise about the Authorization/Authentication framework:

  1. if Camara uses OpenID Connect - ID Tokens are the perfect place to add claims (additional as well, as mentioned in OpenID Connect specification)
  2. if Camara uses the OAUTH2.0 Authorization code grant - Access Tokens represented as JWT is a way to go, but then we need to update all specs (delete openId security scheme and change to oauth2.0)

I think that OpenID Connect is a better choice, due to telco characteristics (there is almost always data resource - telco services subscriber - behind API calls, that requires some kind of authentication/consent).

@AxelNennker
Copy link
Collaborator

AxelNennker commented Dec 21, 2023

Why not return the "unbundled" scope in "scope" in the access token e.g.
{
"iss": https://mycsp.auth0.com/,
"sub": "+46772334567",
"aud": " https://mycsp.com/APIexposureService ",
"exp": 1490922820,
"iat": 1490886820,
“client_id”:0x3445a2,
“azp”:”coolApp”,
"scope": "dpv:fraud-prevention#check-sim-swap dpv:fraud-prevention#retrieve-sim-swap-date"
}

We would standardize all the scopes for all purposes and then there is no need to add a new field.

Resource servers can have a standard implementation for scope handling by continuing to handle static lists of all supported scopes and there is no need to semantic handling of scopes in the RS.

I would like that Camara stays with standard RS implementations. Scopes are proprietary by definition, except the standard scopes defined in OIDC, so we can define whatever we want. But comparing a requested static string to an allowed static string is the "normal" way this is implemented. Semantic handling adds a layer of complexity that gets us into vendor lock-in which Camara should avoid. Adding new fields locks Camara into specific implementations of AZ and RS. I think we should avoid that lock-in.

Also, I would like that Camara uses static scopes in the AZ implementation. Bundled scopes need special processing in the AZ and then again we have a vendor lock-in. Maybe bundled scopes were discussed and decided elsewhere in the past.
A need for proprietary implementations kill projects. Camara should not go there without a good reason.

Maybe there are other reasons to add "purpose" or "reason" to access token. If it is only to avoid long lists of long scopes, I would not go there.

@Elisabeth-Ericsson Elisabeth-Ericsson changed the title Claims to be used in oAuth Access tokens Claims to be used in oAuth Access tokens/ OIDC Identity tokens Dec 21, 2023
@Elisabeth-Ericsson
Copy link
Contributor Author

Hi Axel,
I strongly suggest not to do this.
It will be a nightmare, coming with a huge list of purpose & scope combinations.
The scopes supported by a Camara API are included in the definition. If you want to combine them with all possible purposes, (https://w3c.github.io/dpv/dpv/#vocab-purpose), you will get 78 x data scope combinations.
Our design should be future proof. There might be new purpose values coming up, but this is not the only reason. There is much much more.

There are various reasons to make sure that scopes and purposes are decoupled and properly listed in the claim section of the token.
0) asking the user for consent becomes unmanageble, since you need to present

  1. combining purpose and scope in a new scope string will allow to include multiple purpose and scope combinations in the access token request. If you then just include the result list in the access token, it might be that you get a mix of purposes again, e.g. dpv:service-provisioning#qod-sessions-write , dpv:Advertising#qod-sessions-read. Then you have to specify the actual purpose again when calling the API, such that the API knows the permitted data scopes. As a consequence we are back to custom format and need to change.
  2. the access token content is evaluated by the API implementation. Instead of building the need to decompose purpose and data scopes, the list of data scopes shall be separated out in a clean way.
  3. the purpose for calling an API must be available at API invocation to log it in the API report. This is a critical information to find the pricing information at charging time.
  4. the invocation protocols should be as close to the standards as possible. In the standard, it is clearly stated that the scope is the same scope registered on the authorization server. It is highly questionable whether the huge number originating from all potential scope and purpose combinations can be supported
  5. the API exposure platform at CSPs will have Camara APIs and most probably also (not yet Camara) APIs exposed. The invocation flows, the way of how scopes are registered etc should be the same. Otherwise and aggregator will have to build lots of different token request flows.

Regarding your statement about bundle scopes:
Using "bundle" scopes is a decision on a short term approach. The discussion was driven by Telefonica, Orange and DT and accepted by the other participants engaging in the discussion. Please see issue #32.
I suggest to not reopen this discussion again. Instead we can take the discussion on the ability to pass a scope list in issue #87.

@Elisabeth-Ericsson
Copy link
Contributor Author

@ DT-DawidWroblewski: I agree, I have changed the title of the issue to be more generic. We have to discuss the claims included in the generated JWT token.

@mhfoo
Copy link
Collaborator

mhfoo commented Jan 15, 2024

@DT-DawidWroblewski

Knowing that Camara follows the OpenID Connect framework, we should rather discuss ID Tokens, as JWTs that handle claims, NOT Access Tokens, as these point towards using OAUTH2.0 inside Camara, which is not true.

Referring to CAMARA-AuthN-AuthZ-Concept.md, the section under "Access tokens, ID tokens and refresh tokens": Access Tokens can be encoded as JWT. Does this section need updating?

Access tokens, ID tokens and refresh tokens

Access Tokens: An access token represents the delegated permissions that the resource owner (the user) has given the client application. An access token could contain information about the user who delegated permission, but it does not actually represent the user. An access token also does not represent or give any indication of a user having authenticated. Access tokens should be signed and encoded as JWT (Json Web Token), with the bearer type specified. As access token is intended for a protected resource, the protected resource should be responsible for understanding and validating the token.
ID Tokens: An ID token contains the identity of the resource owner. It describes the authentication event that took place at the identity provider. It contains information such as how and when the user last authenticated. It is always signed and encoded as a JWT.
Refresh tokens: A refresh token is used to reauthorize the granted access by allowing the client application to obtain an access token without the user credentials. The refresh token would be used when the previous access token expires and is no longer valid. An access token generally has a short lifespan to limit the damage that can be done if it were compromised. A refresh token helps improve the experience of the API consumers by not asking them for credentials on a too frequent basis. Protocol Flow
Please note that the ID token mentioned above is not a part of Oauth2 specification. It is defined in the OpenID connect (OIDC) specification described later in the document.

@eric-murray
Copy link
Collaborator

eric-murray commented Jan 16, 2024

Having been involved in authorization code flow implementation for a CAMARA API, I'd like to add some comments for those that might be confused by some of the above discussion:

  • The /authorize endpoint does not return a token of any sort. Rather it returns an authorisation code, which contains no embedded information. The authorization code is then swapped for an Access Token and an ID Token via the POST /token endpoint.
  • The ID Token is only returned because the OpenID Connect specification says it is mandatory. But it is not required to access the service API endpoint, and can be ignored by the API consumer. Only the Access Token is required to access the service API endpoint.
  • Both the Access Token and ID Token MUST NOT contain the end user identity, because this will not necessarily be known to the API consumer, who only requires the information that will be returned by the service API endpoint. Note that the CIBA authorisation flow tokens can contain the login_hint provided by the API consumer in the /bc-authorize call, because this information is already known to them.

EDIT
I should add that, should a login_hint be provided in the /authorize call, a mobile operator will ignore this. Its presence will not alter the flow, and there will be no clue given as to whether the provided value (usually the MSISDN) was correct or not.

@nitroduna
Copy link

From Telefonica’s point of view, we do not need and must not standardize the claims or fields of an access_token, because the definition of an access token is an opaque value in the OAuth specs.

The way the implementation of an API can check if an access token is valid, is an internal implementation detail of each solution (e.g. you can implement the access_token system using a JWT or using a random unique value string keeping the data associated to the generated access_token in your database)

We only can talk about data associated internally to an access_token, but that’s still internal to the implementation. The internal details of the API and the OAuth Server implementation are out of the scope of CAMARA. You can decide to use one or the other system because both have their pros and cons.

Regarding the way an application can see the scopes or purposes the access_token has granted is specified in OAuth 2.0 specs (https://datatracker.ietf.org/doc/html/rfc6749#section-5.1).

The response to the token endpoint where the APP receives the access_token field can also include a scope field. This scope field is mandatory if the scope parameter requested by the APP is different to the granted scope, and you can always include the scope field in the response, even if it's the same. The scopes in CAMARA have been defined with this format:
<purpose>#<technical-scope>

But from the technical point of view, in the OAuth Spec and its grant type endpoints, that's just a scope (a string the APP can include in the authorization request inside a list of strings separated by one space).

So, if the APP asks for <purpose>#<technical_scope> you can include it in the token endpoint response if granted. As this scope can be resolved into a list of additional scopes, they would also be included separated by a space in addition to the requested one.

Example

The application requests a scope with the purpose and technical scope format:
dpv:FraudPrevention#check-sim-swap

The response to the token endpoint would be following:

{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"bearer",
  "expires_in":3600,
  "scope":"dpv:FraudPrevention#check-sim-swap check-sim-swap retrieve-sim-swap-date"
}

The APP can see that dpv:FraudPrevention#check-sim-swap was granted, and also the two additional scopes check-sim-swap and retrieve-sim-swap-date associated to it.

dpv:FraudPrevention#check-sim-swap:
The scope requested by the APP, confirming that it was granted. The scope is exactly the same string requested by the APP, complying with the OAuth2 spec. And this scope is using the agreed format <purpose>#<tenchinal_scope> which includes the purpose.

check-sim-swap:
Additional scope granted when dpv:FraudPrevention#check-sim-swap is granted.

retrieve-sim-swap-date:
Additional scope granted when dpv:FraudPrevention#check-sim-swap is granted.

@DT-DawidWroblewski
Copy link
Contributor

Hi!

@nitroduna makes sense, but then we entering a "grey zone" for CIBA spec, as it is designed for OpenID Connect NOT OAUTH2.0:

The behaviour when the openid scope value is not present is left unspecified by this document, thus allowing for the potential definition of the behaviour in a non-OpenID context, such as an OAuth authorization flow.

CIBA Authentication Request

so either we create a new OIDC CIBA profile, which explains/defines how to use OAUTH2.0 code flow in the context of CIBA, or we leave openid and we comply with the existing standard.

Having MNO characteristics in mind (i.e. subscriber authentication, consent acquisition if relevant), OpenID makes more sense (making Mobile Connect OIDC CIBA profile a complete solution for us).

@AxelNennker
Copy link
Collaborator

Hi Axel, I strongly suggest not to do this. It will be a nightmare, coming with a huge list of purpose & scope combinations. The scopes supported by a Camara API are included in the definition. If you want to combine them with all possible purposes, (https://w3c.github.io/dpv/dpv/#vocab-purpose), you will get 78 x data scope combinations. Our design should be future proof. There might be new purpose values coming up, but this is not the only reason. There is much much more.

There are various reasons to make sure that scopes and purposes are decoupled and properly listed in the claim section of the token. 0) asking the user for consent becomes unmanageble, since you need to present

1. combining purpose and scope in a new scope string will allow to include multiple purpose and scope combinations in the access token request. If you then just include the result list in the access token, it might be that you get a mix of purposes again, e.g.  dpv:service-provisioning#qod-sessions-write , dpv:Advertising#qod-sessions-read. Then you have to specify the actual purpose again when calling the API, such that the API knows the permitted data scopes. As a consequence we are back to custom format and need to change.

2. the access token content is evaluated by the API implementation. Instead of building the need to decompose purpose and data scopes, the list of data scopes shall be separated out in a clean way.

3. the purpose for calling an API must be available at API invocation to log it in the API report. This is a critical information to find the pricing information at charging time.

4. the invocation protocols should be as close to the standards as possible. In the standard, it is clearly stated that the scope is the same scope registered on the authorization server. It is highly questionable whether  the huge number originating from all potential scope and purpose combinations can be supported

5. the API exposure platform at CSPs will have Camara APIs and most probably also (not yet Camara) APIs exposed. The invocation flows, the way of how scopes are registered etc should be the same. Otherwise and aggregator will have to build lots of different token request flows.

Regarding your statement about bundle scopes: Using "bundle" scopes is a decision on a short term approach. The discussion was driven by Telefonica, Orange and DT and accepted by the other participants engaging in the discussion. Please see issue #32. I suggest to not reopen this discussion again. Instead we can take the discussion on the ability to pass a scope list in issue #87.

I do not think that all terms from https://w3c.github.io/dpv/dpv/#vocab-purpose are applicable in Camara. Each Camara API has a small subset of purposes for which this API can be used.
The openid and oauth2 standard grant access based on scope. If we invent something new then we move away from standard AZ implementations and relying party implementation.

The cost for proprietary AZ and RPs is high and it makes it harder for us to switch vendors.

@jpengar jpengar added the v0.2-candidate Custom label for issues that are candidates to be fixed in v0.2 (if needed) label Jan 18, 2024
@Elisabeth-Ericsson
Copy link
Contributor Author

@nitroduna: Hi Pedro, some input on yesterday's discussion.
In the Camara documentation [https://github.com/camaraproject/IdentityAndConsentManagement/blob/main/documentation/CAMARA-AuthN-AuthZ-Concept.md], it is explicitly stated that JWT format has to be used for the access token and identity tokens. It is not the opaque format.
So either we have to change this statement in a version 0.2 of the documentation, or we have to comply to it.
In the latter case - which was my starting point - we need to agree on a JWT token format for the access token and the identity token.
Please check the statement on the id tokens, access tokens and refresh tokens.

@nitroduna
Copy link

nitroduna commented Jan 19, 2024

@Elisabeth-Ericsson

Yes, I see that document has this paragraph

An access token also does not represent or give any indication of a user having authenticated. Access tokens should be signed and encoded as JWT (Json Web Token), with the bearer type specified

So, the document is wrong and that information should be removed. We think those kind of definitions such as access_token, id_token and others, which are already included in the specs, should not be redefined in our documents.

First of all, our solution is an OAuth2 / OpenID Connect solution, so they are built on top of the two main specifications:

The definition of things like access_token or id_token are already included on those specs. We should not contradict the specs, just extend them using the custom mechanism that the spec provides or for example specifying that some optional features the specs are mandatory in CAMARA.

The spec of the access_token that CAMARA should comply is the following: https://datatracker.ietf.org/doc/html/rfc6749#section-1.4

Our specs should have explicit references to the specs on which they are built, and not to replicate or redefine mechanisms and concepts already defined in the base specs. That's the same thing OpenID Connect specification is doing, which references OAuth2 because it's a spec built on top of OAuth2. We should do de same.

Anyway, I'm told that document will be removed in v0.2. It seems the document doesn't represent any agreement. It's a document with inherited recommendations that was used as first steps. So, it doesn't contains any agreement and it will be removed, if my information is correct.

Some technical comments (just in case you need more details of the response above)

According to the following spec extracted from https://datatracker.ietf.org/doc/html/rfc6749#section-1.4 , the access_token should be a value opaque to the app and other external systems.

Access tokens are credentials used to access protected resources. An
access token is a string representing an authorization issued to the
client. The string is usually opaque to the client. Tokens
represent specific scopes and durations of access, granted by the
resource owner, and enforced by the resource server and authorization
server.

It's true that when you have to implement a system with APIs protected with your own OAuth server, you can mainly adopt two approaches: self-contained access_token or stored access_tokens.

Both of the two solutions have very important pros and cons, but they are related to the internal code of your system and they are hidden. That considerations can affect to the security of the solution and the performance. Neither of them are better than the other, and the reasons you can find are very debatable.

If you implement an OAuth system you have to implement two main components:

  • The component that implement the OAuth2 endpoints.
  • The component that protect your APIs with the access_token.

So the requirements for access_token are internal for the system. The developer only have to coordinate the implementation of those two components, so that the mechanism used by the first to generate tokens can be used by the second to validate them and recover all the information required to progress the call to the APIs.

No other external system needs to know how to use that mechanism, and it shouldn't to avoid security issues.

Deciding which mechanism to use and what data to associate with the access token is a developer decision. The two components that need to known that mechanisms are internal to the implementation. APPs using the APIs doesn't need to know that information.

If a developer decides to implement a self-contained solution they can use any other kind of codification and not only JWT, that's another internal implementation decision.

We can see another important defect of what was written in that document:

Access tokens should be signed and encoded as JWT (Json Web Token)

That introduces a potential security problem.

If you generate your self-contained access_token just using JWS (singed JWT), the APP can decode the token and see all the information used by the internal implementation of the OAuth Server.

That is in most of the cases very confidential information which can even contains personal information.

So if you want to use self-contained tokens and you want to use JWT, at least you must use JWE (Encrypted JWT) using a highly protected encryption key that only your system can know about (a problem not found in the stored access-tokens solution).

@jpengar
Copy link
Collaborator

jpengar commented Jan 19, 2024

In the Camara documentation [https://github.com/camaraproject/IdentityAndConsentManagement/blob/main/documentation/CAMARA-AuthN-AuthZ-Concept.md], it is explicitly stated that JWT format has to be used for the access token and identity tokens. It is not the opaque format.
So either we have to change this statement in a version 0.2 of the documentation, or we have to comply to it.

@Elisabeth-Ericsson CAMARA-AuthN-AuthZ-Concept.md is a document inherited from CAMARA Commonalities, and actually this document was written a long time ago to provide generic AuthN/AuthZ recommendations for CAMARA APIs. It provides a generic description of the API GW model, generic explanation and recommendations about authentication and authorization (PKCE, etc...), lists several existing grant types(*)... but actually we didn't use that document in the working group to document the different agreement reached in our discussions. We used CAMARA-API-access-and-user-consent.md, where we explicitly described the agreements reached.

CAMARA-AuthN-AuthZ-Concept.md will be removed after v0.1 as you can see in #103. And for release v0.2 and later, we need to make sure that we document explicit agreements to avoid misunderstandings. The OIDC profile will help a lot with this.

In fact, the use of self-contained JWT access_tokens is something we haven't discussed in the WG in the past, and as Pedro said, that should be an operator implementation decision.

(*)This document actually mentions additional grant types/flows to those considered and defined so far as a valid option to access CAMARA APIs: Auth Code Flow, CIBA and Client Credentials. And that doesn't mean, for example, that we have agreed that we can use all of them...

@Elisabeth-Ericsson
Copy link
Contributor Author

Elisabeth-Ericsson commented Jan 25, 2024

@ALL, but especially @nitroduna and @jpengar:
I have done some investigations on implications on option a) using an opaque access token/ identity token and option b) using self contained access/identity token in jwt format and want to summarize the outcome below (including pro's and cons) for subsequent discussion in the work group.

The most important conclusion from this investigation is that there is impact on the L1 service implementation of each L1 service, which needs to evaluate the scope contained in the token and act accordingly. However the methods of evaluation are different depending on option a) (opaque token) or b) (jwt format). If option a) or b) are the CSPs decision, then an L1 service must be prepared for both options and allow integration to any authorization server in case of a).

Details on option A- opaque token:

  1. A1) The response to the token request (access/id token) contains an opaque token, which is a reference to a token structure in the authZ server.
  2. A2) The response to the token request (access/id token) must also contain the scope. Assumption is that the scope parameter has the format 'dpv:<w3c-purpose value>#<requested technical scope> scope1 scope 2 , listing all scopes granted (including expansion of the bundle scope).
  3. A3) The opaque token is sent on API invocation from API invoker to CSP' exposure platform and forwarded to the API by the gateway. Optionally the gateway validates the token with the authZ server.
  4. A4) The L1 API must call token introspection API from authZ server to understand the content of the access or id token. This means that an L1 service must contact the AuthZ server at runtime every time an API gets called to access end point for token inspection.
  5. A5) The response of the API is a list of key value pairs, one of them being the "scope" key.
  6. A6) The format of the scope key must be specified, current assumption is that it is the same as A2)

Details on option B - self contained token, jwt format

  1. B1) The response to the token request (access/id token) contains a token in jwt format. Eventually this token must be encrypted.
  2. B2) The response to the token request (access token) must also contain the scope. Assumption is that the scope parameter has the format 'dpv:<w3c-purpose value>#<requested technical scope> scope1 scope 2 , listing all scopes granted (including expansion of the bundle scope).
  3. B3) The jwt token is sent on API invocation from API invoker to CSP' exposure platform and forwarded to the API by the gateway. Optionally the gateway validates the token with the authZ server. Eventually the token decryption could happen here as well.
  4. B4) The L1 API must decode the access or id token. If the token is encrypted - and decryption did not happen in prior step, then this must happen now. The L1 API logic must contact the authZ server to decrypt. The resulting token in jwt format contains "scp" claim, indicating the scope. the format of scp could be the list of individual data scopes or the format discussed in A2.

Option b) might have security impacts, if the token is not encrypted.

Independent of the options picked both have impact on the L1 service implementation.
With regards to scope format, we should also acknowledge that Telco operators will expose some proprietary APIs and may be APIs originating from 3GPP (e.g. SEAL) on their platforms. Since we have invented a proprietary scope format, all the other APIs must become aware of this format and the options for sending the information (A or B).

@palmerabollo
Copy link

Thanks @Elisabeth-Ericsson. What you say is right.
The point is that there is no need to standarize the access token format, as it's a CSP implementation detail.

Let me try to illustrate it with a picture:

naas-Page-12 drawio(2)

  1. The app request an access_token.
  2. The AuthServer in the CSP side generates an access_token:
{
  "access_token": "SlAV3......kKG83",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "..."
}

Here, the access_token can be an encrypted jwt token, a UUID pointing to a record in a CSP database, etc. but in all cases the app must treat the token as an opaque string.

  1. The app calls an API, sending the token as a request header. The requests reaches the API Gateway in the CSP side which, depending on its implementation, can decrypt and validate a self-contained token, go to a database to check whether the token is valid, pass it to the API backend to handle or whatever mechanism the CSP has in place.

All the logic regarding token processing is an implementation detail, done in the CSP side. The same applies if there is an aggregator between the App and the CSP.

There are some interesting points in this thread that are still not closed, such as the ID Token format (my suggestion is to discuss it as part of #113 or in a dedicated issue) and the evolution of the short-term agreement (#32) regarding on how to send the purpose.

@Elisabeth-Ericsson
Copy link
Contributor Author

Hi all,
I full agree to the fact that the app must treat the token as an opaque string.
But from the analysis above (on my last post) I see impact on the L1 service implementation for each L1 service.
I don't think that all CSPs are doing the L1 service implementation by themselves.
Thus seeing it from the vendor perspective, it is an important aspect to consider when supplying L1 service implementations.
If we really keep both options open (A and B), then this needs to go into the requirements towards all L1 service implementations.

I also agree to separating the ID token format discussion and the purpose aspect from the access token format.

@AxelNennker
Copy link
Collaborator

There is no conflict between OIDC and OAuth2 regarding access tokens.
For both specs the format is opaque.

Implementers discovered, that having a recommendation how a self-contained access tokens should look like
https://datatracker.ietf.org/doc/html/rfc9068 was standardized in IETF.
Quoting from rfc9068.

Authorization servers and resource servers from different vendors can leverage this profile to issue and consume access tokens in an interoperable manner.

I think this applies to Camara and I believe Camara should use rfc9068.
The access token should be encrypted so that for Clients the access token is opaque.
For validation the access token should be signed, so that the access token can be validated without a network call to a token introspection endpoint.

Adding a purpose field to the access token does not violate any standard. And that is true, whether the access token is self-contrained or an index into some database that then contains the purpose field.

I suggest that Camara access tokens are self-contained and that can be validated without a network call back to the issuing AZ.

Regarding scope values, scope values are in all standards involved a space-delimited list of case-sensitive strings.
If scopes are returned in a response then they are again a list of space-delimited strings. There could be more strings in the returned list of strings than in the scope value of the request.

For the standards it does not matter whether each string has some structure. For the standards it is still a string that is compared character by character, and it does not matter for the AZ nor the RS whether there is a colon in the scope.

@nitroduna
Copy link

nitroduna commented Jan 31, 2024

@Elisabeth-Ericsson I understand what you mean, and I totally agree with that; APIs implementations protected by access_tokens emitted by an OAuth server need to have some mechanism to decide if that access_token has the necessary permissions to call the API.

But still, we think that’s and shall be internal implementation details of each CSP.

That needs coordination between the OAuth Server implementations and APIs implementations (or the component that protect that APIs implementations such as an API GATEWAY). That can be a very internal mechanism, and even more if the provider of the OAuth Server and the Provider of that APIGW is the same, or it can be some mechanism agreed between them if they’re different providers. But no one outside the CSP needs to know about those systems or architectures.

There are lot of systems or architectures to resolve that requirement, regardless of whether the OAuth2 implementation is using self-contained or stored access_tokens, such as:

  • The OAuth2 Server can provide a private introspection APIs only available to the APIs or to the APIGW in front of the APIs to query the data associated to the access_token.
    • That case doesn't only apply to stored access_token, but also to self-contained tokens because they’re encrypted and that avoids sharing encryption private keys.
  • The CSP can provide an API Gateway in front of all its APIs to resolve the access_tokens and to provide the data needed by the API implementations. That’s a very common solution. An APIGW checking the access_tokens to allow or deny access and even sending the internal data associated to the access_token in custom HTTP headers to the APIs.
  • The APIs or the APIGW and the OAuth2 server can share private encryption keys to encrypt and decrypt self-contained access_tokens, which needs coordination because it's highly recommended to automatically rotate those encryption keys every few days.
  • The APIs or the APIGW can have access to the database of stored tokens if implemented by the same provider and deployed in same environments.
  • Etc.

The important thing is that those are internal details. It's all about communication between systems owned by the CSP. Their OAuth Server and their APIs. A CSP doesn’t need to know about the system used by another CSP to allow its APIs to be called with its own generated access_tokens.

In fact, you don’t even need to know the internal format used by other CSPs, you can choose the most efficient format for you APIGW implementation.

For example, a CSP may decide to parse those scopes formatted as dpv:<w3c purpose value>#<requested technical scope> directly into its OAuth Server, and store that information already divided into its parts, to make it easy its APIGW or API implementations.

@Elisabeth-Ericsson
Copy link
Contributor Author

Thank you for all of your comments. To summarize the discussion I want to separate the 1) access token handling from the 2) access token content format discussion.
With regards to 1) (Access token handling) I understand that both methods: opaque access token and encrypted JWT access token must be support. We need to document both of the methods in https://github.com/camaraproject/IdentityAndConsentManagement/blob/main/documentation/CAMARA-AuthN-AuthZ-Concept.md or merge it into https://github.com/camaraproject/IdentityAndConsentManagement/blob/main/documentation/CAMARA-API-access-and-user-consent.md.
With regards to 2) content of the generated access token resp access token structure in authZ, I need an urgent clarification and agreement.
Proposal for scope handling is to have the permitted data scopes expanded and separated by blank and exactly in line with the data scopes documented in the API spec.
Proposal for purpose handling is to have a separate access token structure parameter which carries the purpose.

@jpengar
Copy link
Collaborator

jpengar commented Feb 1, 2024

Thank you for all of your comments. To summarize the discussion I want to separate the 1) access token handling from the 2) access token content format discussion. With regards to 1) (Access token handling) I understand that both methods: opaque access token and encrypted JWT access token must be support. We need to document both of the methods in https://github.com/camaraproject/IdentityAndConsentManagement/blob/main/documentation/CAMARA-AuthN-AuthZ-Concept.md or merge it into https://github.com/camaraproject/IdentityAndConsentManagement/blob/main/documentation/CAMARA-API-access-and-user-consent.md. With regards to 2) content of the generated access token resp access token structure in authZ, I need an urgent clarification and agreement. Proposal for scope handling is to have the permitted data scopes expanded and separated by blank and exactly in line with the data scopes documented in the API spec. Proposal for purpose handling is to have a separate access token structure parameter which carries the purpose.

In my opinion:

For point 1) CAMARA should NOT document anything precisely because it does not standardize the implementation. CAMARA doesn't have to say that JWT approach, database approach or any other solution has to be supported.

For point 2), if you are referring to the /token response, we have already explained Telefonica's position in previous comments (#100 (comment)). The purpose information is already present in the scope list returned, since it contains the scope string requested by the client, which includes the "dpv:xxxxx" format.

@AxelNennker
Copy link
Collaborator

My summary and understanding of the above:

OAuth2 states this about access tokens https://datatracker.ietf.org/doc/html/rfc6749#section-1.4

Tokens represent specific scopes and durations of access, granted by the resource owner, and enforced by the resource server and authorization server.

AZ and RS MUST have an understanding about the access token because otherwise the RS is not able to validate the access token.

  1. It might be that the AZ has a token introspection endpoint, that returns the access token's content (especially scopes) if it is valid.
  2. It might be that AZ and RS agreed on self-contained tokens and a common jwt format. The RS can descrypte and validate the access token and "see" scope which MUST be part of the accesstoken because otherwise authorization enforcement is not possible.
  3. It might be that the AZ has an token-validation endpoint that only answers valid/invalid.

From enforced by the resource server we conclude that for the cases 1) and 2) the RS knows the scopes because otherwise the RS could not enforce. So, if purpose is encoded in scope then the RS knows purpose.

I assume your (@Elisabeth-Ericsson ) concern is with implementation option 3) because then the RS does not know scope nor purpose, right?

Do current implementations use option 3)?

@Elisabeth-Ericsson
Copy link
Contributor Author

@AxelNennker: thank you for the detailed analysis. Case 3) is the problem.
In case 1) and 2) the RS knows the scopes.

  1. When following the short term agreement on sending purpose concatenated to the scope, then also the purpose is know.
  2. In the future, when opening up this limitation, and sending purpose in a separate request parameter (as standardized by openid-connect-4-identity-assurance-1_0-13 ), then the purpose should be in a separate claim (for jwt format, case 2) or a separate attribute (for opaque tokens in case 1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation get fixed by PR #121 v0.2-candidate Custom label for issues that are candidates to be fixed in v0.2 (if needed)
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants