Core 2 / 3.1.3.7 - azp claim underspecified and overreaching

Issue #973 resolved
Brian Campbell created an issue

From the definition of "aud" in JWT and its use in Connect's ID Token (relevant spec text is copied below), it seems that that the client id of the client/RP that made the authentication request has to be one of the values, or the only value, of the "aud" claim in the ID Token. That's logical and consistent and provides reliable and interoperable guidance to implementers about producing and consuming the ID Token. I think that the client id of the RP/client that made the authentication request should always be represented in the aud of the returned ID Token.

The text around "azp" in the ID Token section and the ID Token Validation section seems to maybe suggest something different, however. Like perhaps that the client id of the RP/client that made the authentication request could, in some totally unspecified circumstance, be the value of the azp claim and that the aud would not identify that client as an intended recipient. Am I misinterpreting things? I hope so because that seems like it'd be fragile from an interop perspective, would contradict RFC 7519, and is certainly more cumbersome for general JWT libraries to support.

from http://tools.ietf.org/html/rfc7519#section-4.1.3

 4.1.3.  "aud" (Audience) Claim

   The "aud" (audience) claim identifies the recipients that the JWT is
   intended for.  Each principal intended to process the JWT MUST
   identify itself with a value in the audience claim.  If the principal
   processing the claim does not identify itself with a value in the
   "aud" claim when this claim is present, then the JWT MUST be
   rejected.  In the general case, the "aud" value is an array of case-
   sensitive strings, each containing a StringOrURI value.  In the
   special case when the JWT has one audience, the "aud" value MAY be a
   single case-sensitive string containing a StringOrURI value.  The
   interpretation of audience values is generally application specific.
   Use of this claim is OPTIONAL.

from http://openid.net/specs/openid-connect-core-1_0.html#IDToken

 ... 
  aud
    REQUIRED. Audience(s) that this ID Token is intended for. It MUST contain
    the OAuth 2.0 client_id of the Relying Party as an audience value. It MAY also
    contain identifiers for other audiences. In the general case, the aud value is an
    array of case sensitive strings. In the common special case when there is one 
    audience, the aud value MAY be a single case sensitive string. 
 ...

  azp
    OPTIONAL. Authorized party - the party to which the ID Token was issued.
    If present, it MUST contain the OAuth 2.0 Client ID of this party. This Claim
    is only needed when the ID Token has a single audience value and that audience
    is different than the authorized party. It MAY be included even when the authorized
    party is the same as the sole audience. The azp value is a case sensitive string
    containing a StringOrURI value. 
 ...

from http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation

Clients MUST validate the ID Token in the Token Response in the following manner:
...
  3. The Client MUST validate that the aud (audience) Claim contains its client_id
    value registered at the Issuer identified by the iss (issuer) Claim as an audience. 
    The aud (audience) Claim MAY contain an array with more than one element.
    The ID Token MUST be rejected if the ID Token does not list the Client as a
    valid audience, or if it contains additional audiences not trusted by the Client. 
  4. If the ID Token contains multiple audiences, the Client SHOULD verify that an 
    azp Claim is present.
  5. If an azp (authorized party) Claim is present, the Client SHOULD verify that its
    client_id is the Claim Value.
...
  8 If the JWT alg Header Parameter uses a MAC based algorithm such as HS256,
    HS384, or HS512, the octets of the UTF-8 representation of the client_secret 
    corresponding to the client_id contained in the aud (audience) Claim are used
    as the key to validate the signature. For MAC based algorithms, the behavior is
    unspecified if the aud is multi-valued or if an azp value is present that is 
    different than the aud value.
...

Comments (21)

  1. Nat Sakimura

    Semantic difference between aud and azp actually is quite clear. aud indicates who is allowed to consume it, and azp indicates who is allowed to present it.

    In case of ID Token, it is the client who is supposed to consume it, so the aud needs to include the client. If it were to be passed down to some other client, which we do not advise by the way, then the receiver should verify who has given it to him as well as if that transfer is legitimate or not. azp helps around it. The receiver should look at azp claim value and match that to the identity of the presenter. Note: the receiver needs to have some way of authenticate the presenter.

    This is outside of the scope of the OpenID Connect.

    Originally, it was discussed as a claim in JWT. However, some people did not want it to be in JWT but said it would be OK to be in the Connect spec. I was quite unhappy about it, but at the time it seemed to be better to be defined somewhere rather than not, as it was already being started to be used in Google play store etc.

    Let us think about the token agent case:

    • AS: authorization server
    • TA: token agent
    • CL: client who wants the user authenticated.

    Then the flow would be:

    title Token Agent usecase
    
    participant client as CL
    participant Token Agent as TA
    participant Authorization Server as AS
    
    CL->TA: get ID Token
    TA->AS: get ID Token
    AS-->TA: ID Token (sub=userid, aud=CL, azp=TA)
    TA->CL: ID Token (sub=userid, aud=CL, azp=TA)
    CL->CL: Check the calling party = TA. 
    CL->CL: Check if azp of the ID Token points to TA. 
    CL->CL: Check if aud of the ID Token points to CL. 
    

    tokenagent.png

    If this is correct, then the spec seems to be wrong. It is talking about some other kind of JWT assertion, as it seems.

    Still waiting google response though.

  2. Michael Jones

    I believe that you have the role assignments reversed in your diagram above, Nat. The "Token Agent" in your diagram is the client that is directly requesting the ID Token, and so is the audience. This party may also pass the token on to another party to whom the token is being issued, which is identified by the "azp" claim.

    This is supported by the notes from the 6-May-13 in-person working group meeting, in which we (including Breno) agreed that "azp" would represent an issued-to claim, identifying the party that the client requesting the token is intended to pass the token to. This party is the "client" in your diagram above. The "Token Agent" in your diagram is the requesting client, and thus the audience of the token.

    The requesting client is clearly required to verify that it is an audience of the token. Your role assignment above would break this invariant, causing interop to fail. We cannot do this - particularly as an errata action.

    Also, you're talking about a presenter, whereas the definition of "azp" says nothing about a presenter. The azp definition is "OPTIONAL. Authorized party - the party to which the ID Token was issued. If present, it MUST contain the OAuth 2.0 Client ID of this party. This Claim is only needed when the ID Token has a single audience value and that audience is different than the authorized party." I realize that informally we may have thrown the term "authorized presenter" around in some discussions, but the azp claim represents a party that the token is being issued to - not a presenter.

    The real bug here is this text in 3.1.3.7 (ID Token Validation): "5. If an azp (authorized party) Claim is present, the Client SHOULD verify that its client_id is the Claim Value." The problem here is that "the Client" being referred to is the not the requesting client, which is also called "the Client" in "3. The Client MUST validate that the aud (audience) Claim contains its client_id value registered at the Issuer identified by the iss (issuer) Claim as an audience." We need to clarify that "the Client" in item 5 is the issued-to client identified by the "azp" claim - not the requesting claim identified by the "aud" claim.

  3. Michael Jones

    Here's a possible resolution, addressing the ambiguity of the term "the Client". Change "If an azp (authorized party) Claim is present, the Client SHOULD verify that its client_id is the Claim Value" to "If an azp (authorized party) Claim is present, the Client SHOULD verify that the client_id of the party to which the token was issued is the Claim Value". We clearly have to differentiate the two clients, because the text as it is clearly wrong, no matter which assignment of roles you subscribe to, because it implies that the two Client IDs must be the same in the verification language above. Thoughts?

  4. Nat Sakimura

    It is related to #712 and #830. The #830 was largely a dup of #712 so it was superseded. Apparently, the text decided on #712 was changed without being ticketed.

    The approved and resolved text was:

      azp
       OPTIONAL. Authorized Presenters.
       This member identifies OAuth 2.0 Client(s) and potentially
       other parties authorized to use this ID Token as an assertion
       of the identity of the ID Token's subject at the ID Token's audiences.
       If present, it MUST contain the client_id or other identifiers for 
       all Authorized Presenters.
       The issuer is not to be listed as an Authorized Presenter.
       This Claim is only needed when the party requesting the ID Token
       is not the same as the sole audience of the ID Token.
       It MAY be included even when the Authorized Presenter is the same
       as the sole audience.
       Authorized Presenter values should be verified by the participants,
       however the mechanisms for validating azp values are beyond the scope of this specification.
       In the general case,  the azp value is an array of
       case sensitive strings, each containing a StringOrURI value.
       In the special case when the ID Token has one authorized presenter,
       the azp value MAY be a single  case sensitive string containing a StringOrURI value.
    

    This, however, ended somehow as:

      azp
        OPTIONAL. Authorized party - the party to which the ID Token was issued.
        If present, it MUST contain the OAuth 2.0 Client ID of this party. This Claim
        is only needed when the ID Token has a single audience value and that audience
        is different than the authorized party. It MAY be included even when the authorized
        party is the same as the sole audience. The azp value is a case sensitive string
        containing a StringOrURI value. 
    

    The approved text seems to be much more detailed and clearer. Do you have any idea why it got changed to the present form?

  5. Michael Jones

    The “azp” description was updated after the in-person OpenID Connect working group meeting at Google on 6-May-13, in which we agreed that “azp” would be used to represent the issued-to information, and that the claim would be named “Authorized Party”. See the meeting notes at http://lists.openid.net/pipermail/openid-specs-ab/Week-of-Mon-20130506/003466.html, including attachment http://lists.openid.net/pipermail/openid-specs-ab/attachments/20130508/6d9b0ac0/attachment-0005.jpg, which documents that we decided to represent issued-to information with “azp” and attachment http://lists.openid.net/pipermail/openid-specs-ab/attachments/20130508/6d9b0ac0/attachment-0006.jpg, which documents that the “azp” claim is to represent an “Authorized Party”. We also decided to make “azp” single-valued there, where it had previously been multi-valued.

    Breno de Medeiros and Naveen Agarwal of Google were at the meeting, and in fact, were behind many of these changes, which is significant, since they were the inventors of this functionality. They concurred with and participated developing these resolutions previously open issues about the meanings of “aud” and “azp”. Afterwards, the meeting notes were sent to the working group mailing list for review there and no objections were raised.

    Furthermore, all of this text went through both the 45 day Implementer’s Draft 2 public review period announced at http://openid.net/2013/06/07/review-of-proposed-openid-connect-implementers-drafts/ and the 60 day Final Specification public review period announced at http://openid.net/2013/12/20/review-of-proposed-final-openid-connect-specifications-and-implementers-drafts/, and no objections were raised during those reviews either. People seemed happy with the resolution arrived at during the working group meeting.

    So in conclusion, it would probably be less confusing to all concerned if people were to stop referring to “azp” as “authorized presenter” or ascribing those semantics to it (whatever those may be). Sometime before Implementer’s Draft 2 of OpenID Connect that terminology was used, but the working group changed that and the meaning of “azp” by consensus in May 2013 and it’s been that way ever since. Trying to overload “azp” with a different meaning than what’s in the standard seems counterproductive.

  6. Nat Sakimura

    I have recorded the outcome of the May 6, 2013 F2F in #712. It is still very confusing.

    Re: the figure above:

    Mike, so you are saying that it should be like:

    title Token Agent usecase
    
    participant client as CL
    participant Token Agent as TA
    participant Authorization Server as AS
    
    CL->TA: get ID Token
    TA->AS: get ID Token
    AS-->TA: ID Token (sub=userid, aud=CL, azp=TA)
    TA->CL: ID Token (sub=userid, aud=CL, azp=TA)
    CL->CL: Check the calling party = TA. 
    CL->CL: Check if azp of the ID Token points to TA. 
    CL->CL: Check if aud of the ID Token points to CL. 
    

    aaaa.png

    Is that right? I do not understand why just aud would not do the job in this case.

  7. Michael Jones

    Close. My understanding of the intended usage of "azp" was as follows:

    CL->TA: get ID Token (doing something analogous to client authentication to the TA with CL's Client ID)
    TA->AS: get ID Token (doing client authentication to the AS with TA's Client ID)
    AS->TA: ID Token (sub=userid, aud=TA, azp=CL)
    TA->TA: Verify that the aud of the ID Token is the TA's Client ID
    TA->CL: ID Token (sub=userid, aud=TA, azp=CL)
    CL->CL: Verify that the azp of the ID Token is the CL's Client ID
    

    More concisely, the party making the request to the AS is the audience of the resulting token - just like always. "azp" is there to identify another party (the "issued-to" party) that the token may also be sent to by the requesting client.

    At the time, including at the in-person May 2013 meeting at Google, I tried to make the case that life would be simpler and the security guarantees clearer if both parties were just listed as audience values. Note that this also extends cleanly to cases with even more parties, where all the ID token recipients would be just be listed as audience members. But because Google had already deployed something using a second claim, we decided to at least sanction this by listing "azp" in the spec, while leaving how to get an ID Token with different "aud" and "azp" values out of scope.

    This is increasingly looking like a mistake on our part, but we did it. I still think that we should recommend using multi-party audience values for multi-party scenarios, rather than recommending any further use of "azp". It seems to be the third rail of OpenID Connect - causing confusion whenever we touch it, rather than bringing clarity.

    That being said, I could easily have the "aud" and "azp" assignments reversed relative to what Google actually does, as could you. I think we really don't know. Before we do anything substantive to any of the "azp" text, I think we should get Breno, Naveen, and William to clearly say what they actually do when. Ideally, I'd like to see actual protocol traces, including the Authentication Request, the Authentication Response, and the communication between the Client that made the request to the AS and any other Clients that it also sends the resulting token(s) to. Among other things, that would also answer OAuth-y questions like which Client ID is being used for client authentication.

    I'll ask them for this, as input to us hopefully being able to resolve this in the best way possible.

    But I really wish we'd left "azp" out and just recommended that all parties receiving the ID Token be audience members.

    Cheers, -- Mike

  8. Brian Campbell reporter

    I likewise think we'd be better off, if "azp" had been left out.

    One of the main reasons I pushed for allowing aud in JWT to allow for multiple values was to try and provide an alternative to putting something like azp in Connect. We ended up with both though. And need to move forward.

    Hopefully the Connect language around azp can be cleared up in a way that makes sense for the normal majority of implementations - i.e. not having variant rules for audience processing based on the presence and value of the azp claim (which I think would contradict RFC 7519 and language around aud in Connect Core). But I suppose that we need to understand what Google actually did with it first. And potentially then face a decision on whether or not to codify their implementation choices into the spec. Hopefully, however, it can be done cleanly.

  9. Brian Campbell reporter

    The related 'Your help needed clarifying Google's "azp" claim usage' thread on the mailing list suggests that Google's use of azp is in support of issuing an ID token for cross-client usage where there's an Android app with a back-end server. The app and the server each have their own client identifiers. The Android app requests the token and the app's client id is the azp of the resulting ID token. The Android app also somehow includes the client id of back-end server in the request, which becomes the sole aud value in the resulting ID token. The app can present the token to the back-end, which can validate that it is an intended audience and see that the app is an "authorized party". I don't know if the app actually validates the ID token itself. I can (sort of) see the logic behind what they did, which seems consistent with this text in the azp definition, "This Claim is only needed when the ID Token has a single audience value and that audience is different than the authorized party." However, having the client that made the authentication request not be listed as a valid audience in the ID Token is contradictory to the definition of aud in JWT as well as the text in Connect that says, "[aud] MUST contain the OAuth 2.0 client_id of the Relying Party as an audience value" and "The Client MUST validate that the aud (audience) Claim contains its client_id value registered at the Issuer identified by the iss (issuer) Claim as an audience."

    Earlier in that same mailing list thread Adam Dawes says that, "this underlying protocol is not standard OAuth2 or OIDC". I'd argue then that there's no reason to try and retrofit the azp/aud processing rules in Connect to fit what Google did for their own non-standard authentication protocol. Rather Connect should strive for something that's consistent and easily comprehensible.

  10. Michael Jones

    Mike will take a stab at slightly revised wording following Brian's suggestions. Then the working group will review the revised wording.

  11. John Bradley

    The core thing we need to have in the Connect spec is what to do if a connect client receives a token with a azp value.

    For the most part that should be reject it unless you are using a extension that defines it.

    I think we said too much about what the safe value is, and possibly people started putting it in the id_tokens when it is not required.

    It should not be in a connect_id token unless the client is using some other extension.

  12. Brian Campbell reporter

    In the normal Connect flow Google issues ID tokens with the RP/client id as the value for both aud and azp. So "reject it unless you are using a extension that defines it" is probably too far. Ignoring is probably more appropriate guidance at this point and I suspect is what most clients are doing.

  13. John Bradley

    Ignoring AZP potentially allows tokens to be issued to 3rd parties that can be used to impersonate the subject,

    There was a security reason for warning clients to reject JWT they receive as id_tokens that were not issued to them directly.

    The other alternative is to remove AZP from the spec to discourage people from using it, and hope that Google has tight enough issuance rules that no one finds a security hole.

  14. Peter Andrews

    Expanding on the post mentioned above, Google uses the azp claim to represent the presenting party in both normal (single client) and cross-client (where a mobile client requests an ID token intended for its server client [a confidential web client] in the same project) OpenID Connect flows.

    In the normal flow, the azp claim has the same value as the aud claim, that being the client ID of the requesting party.

    In the cross-client case, the mobile client includes an additional audience parameter in the auth and token requests, with the value set to the server's client ID. The ID token returned with the token response contains an azp claim with the value of the requesting party (the mobile client) and an aud claim with the value of the audience parameter (the server client). Our OpenID Provider will only issue such a token if both the originating client, and the client specified as the audience are in the same parent project, otherwise a 400 error is returned.

  15. William Denniss

    What follows is my interpretation of authorized party after reading the spec for every reference to azp, and considering this problem in the context of AppAuth's ID Token validation rules.

    I interpret the spec to imply that there are essentially 3 valid formats for specifying the audience(s) of the ID Token. Those are, Single (aud is a string, azp is equal to aud if it is present), Multiple (aud is an array), and what I will call "Dual" (aud is a string, azp is present and not equal to aud ).

    It’s easier to explain using examples, so here are examples of each of the 3:

    Single Audience Format

    An ID Token with a single audience. The most common form, needs not have azp , but it can, and if so must match aud .

    {
      "aud": "foo",
      "azp": "foo"
      // ...
    }
    

    Figure 1: Single Audience ID Token

    {
      "aud": "foo",
      // ...
    }
    

    Figure 2: Single Audience ID Token (with implied azp). Semantically equal to Figure 1.

    Multiple Audience Format

    An ID Token has multiple audiences. One of those audiences is listed as the azp, being (I infer), the client who directly received the token from the OP.

    {
      "aud": ["foo", "bar", "baz"],
      "azp": "bar"
      // ...
    }
    

    Figure 3: Multiple Audience ID Token

    Note that according to § 3.1.3.7. rule 4, unlike the Single Audience ID Token, a Multiple Audience ID Token needs to contain an azp to be considered valid.

    Dual Audience Format

    This is a special-case variant of the Multiple Audience Format, for the case where the ID Token has exactly 2 audiences.

    {
      "aud": "foo",
      "azp": "bar"
      // ...
    }
    

    Figure 4: Dual Audience ID Token

    {
      "aud": ["foo", "bar"],
      "azp": "bar"
      // ...
    }
    

    Figure 5: Multiple Audience ID Token with 2 audiences. Semantically equivalent to Figure 4.

    Discussion

    I’ve not seen any disagreement so far that the Single, and Multiple forms exist. Dual on the other hand is slightly ambiguously defined today, however I base its existence on this line: in Section 2 “This Claim is only needed when the ID Token has a single audience value and that audience is different than the authorized party”.

    This implies that it is valid to have an audience different to the authorized party. Since elsewhere it is specified that the authorized party is one of the audiences (specifically, for the Multiple Audience form), I infer that the authorized party is also considered semantically one of the audiences.

    This is also how Google’s OP implemented the spec. Specifically, Google’s OP uses the Dual Audience format for a case where there were two valid audiences (mobile and server clients in the same project).

    Perhaps in hindsight it’s needlessly complicated having three ways to represent the one thing. However, that’s the world we live in, and I do believe this is what the spec documents.

    To correct the internal inconsistency cause by the rules in Section 3.1.3.7. ID Token Validation, which conflict with the definitions in Section 2, I propose the following Errata be adopted:

    Proposed Errata

    1/

    Mike wrote earlier:

    Here's a possible resolution, addressing the ambiguity of the term "the Client". Change "If an azp (authorized party) Claim is present, the Client SHOULD verify that its client_id is the Claim Value" to "If an azp (authorized party) Claim is present, the Client SHOULD verify that the client_id of the party to which the token was issued is the Claim Value". We clearly have to differentiate the two clients, because the text as it is clearly wrong, no matter which assignment of roles you subscribe to, because it implies that the two Client IDs must be the same in the verification language above. Thoughts?

    I agree that this change proposed by Mike is needed, and resolves the ambiguity of § 3.1.3.7. rule 5, which currently would cause every client but the one specified as the azp client to reject the token in the case of a Multiple Audience ID Token, which I don’t believe was the intent of the spec.

    2/

    § 3.1.3.7. rule 3 also needs to be corrected, to correctly validate a Dual Audience ID Token defined by Section 2.

    FROM:

    “The Client MUST validate that the aud (audience) Claim contains its client_id value registered at the Issuer identified by the iss (issuer) Claim as an audience. The aud (audience) Claim MAY contain an array with more than one element. The ID Token MUST be rejected if the ID Token does not list the Client as a valid audience, or if it contains additional audiences not trusted by the Client.”

    TO (changes emphasized):

    “The Client MUST validate that the aud (audience) Claim contains its client_id value registered at the Issuer identified by the iss (issuer) Claim as an audience, or that the azp (authorized party) Claim is equal to its client_id value. The aud (audience) Claim MAY contain an array with more than one element. The ID Token MUST be rejected if the ID Token does not list the Client as a valid audience or authorized party, or if it contains additional audiences not trusted by the Client.”

    3/

    Section 2 contains text that is overly restrictive in documenting when the azp is needed. azp is also relevant in the case where there are multiple audiences, to indicate which audience from the list that the token was initially issued to (referred to in § 3.1.3.7. rule 4).

    FROM:

    “This Claim is only needed when the ID Token has a single audience value and that audience is different than the authorized party”

    TO:

    “This Claim is needed when the ID Token has a single audience value and that audience is different than the authorized party, or the ID Token has multiple audience values”

  16. Log in to comment