Re: [Txauth] Polymorphism (Was: JSON Schema?)

Dick Hardt <> Thu, 09 July 2020 01:31 UTC

Return-Path: <>
Received: from localhost (localhost []) by (Postfix) with ESMTP id 3838E3A0B5E for <>; Wed, 8 Jul 2020 18:31:42 -0700 (PDT)
X-Virus-Scanned: amavisd-new at
X-Spam-Flag: NO
X-Spam-Score: -2.096
X-Spam-Status: No, score=-2.096 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, HTML_FONT_LOW_CONTRAST=0.001, HTML_MESSAGE=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, URIBL_BLOCKED=0.001] autolearn=ham autolearn_force=no
Authentication-Results: (amavisd-new); dkim=pass (2048-bit key)
Received: from ([]) by localhost ( []) (amavisd-new, port 10024) with ESMTP id pWZq_P0avZIK for <>; Wed, 8 Jul 2020 18:31:39 -0700 (PDT)
Received: from ( [IPv6:2a00:1450:4864:20::22d]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by (Postfix) with ESMTPS id 1A1C53A0B5B for <>; Wed, 8 Jul 2020 18:31:39 -0700 (PDT)
Received: by with SMTP id s9so447167ljm.11 for <>; Wed, 08 Jul 2020 18:31:38 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=6EIWDn6Pd2qhr0KrJ7JfYiTDS27OfIEmyasJMZekISA=; b=mQUeQOFHNMdJQ6Z0x1G1Gq0xXiS7zhNd8yTCogUJEEdoQBabuz7ruh52eHIx5avDxc 1o+N3Yo1pfLQK46wHJNYaeOlHCXxodADkVUr6mqQCSvMmvFgouP6z+8eRBVNEoVNtrGn VzNIjJWMnGNCvnh9Cv8KTTlyIlJOwhOltegUxSS7hfBc2totixZ1SPe4SnJ8YVNGnxAx 1pR0pZc3y+VqHzvxmrqjrjG9oNkvVn/kZBe1i9yGMOXPIud8Qvrm0y+jQGVTo/Pa4wcR h+3BV/5vy4p1JubNy2x8mtbToIE4Vla03MW9R3gTqWflOoVNVMIzDgxqsbSrrwW2Rl+v WuHA==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=6EIWDn6Pd2qhr0KrJ7JfYiTDS27OfIEmyasJMZekISA=; b=muu79AvHK9W2yMJqHUKlELAfyJ9vNFJ97j3PiZ16y8FJDKsoqAbOHQOs7yGtt4TXEG IEv4ee5ipq2Q1lQQtdlH5il1RKg+IhD4rxvOffyG80529LXCNz86L4jVAkrtaRl80VIJ mBkwjIg4zJxaLc8JqrRWXT4BWmIrtWcLeGwEQZg2RYpwh66jC9uZtYmUw+PYY6tAfBxX 0rYXFg3QyNoTbMpLMFA0pGUP6W7ZliRyTT7BFU0CBe2urtX0dJ9yOWyYP5DlyEv5idQQ 0rHaW/PcdRHktREkFvH9AZw1t464HJsrH0reV8lDcsNKjj8DNQMqe7i/ixPBoHLEMDvd hOyA==
X-Gm-Message-State: AOAM530LJm9t3f1PPo1B6qXb0yyqm55xYebB5luMqg+EjNHZVlsEGSaL cHdXxlN9Y6jm8dTCY+7gRvxyZ3xoQofhDXdZn4E=
X-Google-Smtp-Source: ABdhPJyYZxAowcZNJgt/8qBc+3Te2h4pSGy5gJbMTwLR9NsgGDzDx0e4AGdDUZSb53ydcGeIgYBVg/ZSuu0Cby8/PBc=
X-Received: by 2002:a05:651c:547:: with SMTP id q7mr33216448ljp.437.1594258296978; Wed, 08 Jul 2020 18:31:36 -0700 (PDT)
MIME-Version: 1.0
References: <> <> <> <> <> <> <> <> <> <> <> <> <>
In-Reply-To: <>
From: Dick Hardt <>
Date: Wed, 8 Jul 2020 18:31:00 -0700
Message-ID: <>
To: Justin Richer <>
Content-Type: multipart/alternative; boundary="00000000000078c52b05a9f82f9e"
Archived-At: <>
Subject: Re: [Txauth] Polymorphism (Was: JSON Schema?)
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: <>
List-Unsubscribe: <>, <>
List-Archive: <>
List-Post: <>
List-Help: <>
List-Subscribe: <>, <>
X-List-Received-Date: Thu, 09 Jul 2020 01:31:42 -0000

I had intended to provide a code sample comparing XYZ request type with
XAuth request type

Let's say an AS only supports OAuth scopes (does not support RAR). Here is
JS code to check:

//XYZ code

var oauth_scope = true;
requests.forEach( item => {
    if (typeof item === "object")
        oauth_scope = false;
if (!'oauth_scope') {
    // return error

// XAuth code

if ('oauth_scope' != authorizations.type) {
   // return error

Here is some JS code that for an AS that supports OAuth scopes, RAR, and
OIDC requests:

// XYZ

if (request) {
    requests.forEach( item => {
        if (typeof item === "object") {
            // process a RAR item
        } else if(typeof item === 'string') {
            // process a scope item
        } else {
            // throw an error
   // process the whole request
if (oidc_claims_query) {
    // process oidc request


if (authorizations) {
    switch (authorizations?.type) {
        case 'oauth_rar':
            // process authorizations.details - RAR
        case 'oauth_scope':
            // process authorizations.scope
            // process the whole request
        case 'oidc':
            // process OIDC claims
            // error for unknown type

Understanding what the code is doing looks much clearer in XAuth to me.


On Wed, Jul 8, 2020 at 3:55 PM Dick Hardt <> wrote:

> Hey Justin,
> Just because we are using OIDC claims, does not mean we need to mimic the
> OIDC request and response.
> I was envisioning a grant request could look as the following (using XAuth
> syntax):
> {
>     "authorizations": {
>         "type":"oidc",
>         "claims": ["name", "picture"]
>     },
>     "claims":{
>         "oidc": {
>             "id_token" : {
>                 "email"          : { "essential" : true },
>                 "email_verified" : { "essential" : true }
>             },
>             "userinfo" : {
>                 "name"           : { "essential" : true },
>                 "picture"        : null
>             }
>         }
>     }
> }
> Of course a developer could choose to only ask for a subset of this.
> Note the new authorization type of "oidc", that takes "claims" rather than
> "scope".
> This keeps all the authorizations and claims in their respective request
> and response containers.
> We had a thread months ago on the OIDC two stage model, and I still fail
> to see why forcing that has any advantage.
> /Dick
> On Wed, Jul 8, 2020 at 3:25 PM Justin Richer <> wrote:
>> I’m glad that we can agree that there are a number of things in legacy
>> protocols that are unfortunate side effects of the circumstances in which
>> they were built. Space-separated scope strings, for instance, would fall in
>> that category, as I’ve previously explained.
>> A key point in the below: the OIDC “claims” request already mixes user
>> claims (returned in an API) and authorization (to fetch user claims from an
>> API), so that ship has sailed if you’re using it. It doesn’t make sense to
>> have it under a “claims” or “authorizations” request, since it’s a query
>> language that affects both. Maybe you’d call this another “unfortunate
>> design”, but the context was about re-using an externally-defined query
>> language that was not made for GNAP.
>> My scenario was for someone who is already using “claims” and wants to
>> keep using it. (The vast majority of OIDC implementations, in my
>> experience, don’t use that feature, and it’s not even required to be
>> implemented by the AS, but again we’re talking about using this feature as
>> an example of an external query language — and there are others out there.)
>> In GNAP, you should return claims directly in the response, and request
>> specific elements from the APIs protected by the access token. These are
>> separate things, and by design both XAuth and XYZ have put them into
>> separate containers in the request. This is a good design, and so putting
>> something that conflates these two aspects into one or the other of the
>> containers is not a particularly good option, in my opinion.
>> Additionally, though this is a bit of an aside and getting into the
>> specifics of identity, the “claims” parameter of ODIC is a query language
>> bound to the full user profile. It is my stated position that the items
>> coming back from the AS should only be identifiers, and not full profile
>> information. The reasoning is pretty simple: the client doesn’t know what
>> profile information it needs until it knows who the user is, so putting
>> that into a protected API like the UserInfo Endpoint makes much more sense
>> than putting it into the immediate response, where it is not needed,
>> because the client already knows it. The AS doesn’t know what the client
>> needs to know, either, so it can’t determine what to fulfill. OIDC’s
>> two-stage model makes sense, and GNAP should really only focus on enabling
>> this.
>>  — Justin
>> On Jul 8, 2020, at 6:10 PM, Dick Hardt <> wrote:
>> On Wed, Jul 8, 2020 at 1:02 PM Justin Richer <> wrote:
>>> On Jul 8, 2020, at 3:16 PM, Dick Hardt <> wrote:
>>> I think representing the request as an array is simplistic, and
>>> complicated at the same time.
>>> On the simplistic front, as there is no clear mechanism for extending
>>> the request with properties that apply to all of the request.
>>> The elements of the array are taken as a set, to be tied to the same
>>> resulting access token. If one of those elements is defined, by the AS
>>> and/or the RS’s it’s protecting, to apply across some or all of the other
>>> elements, then that’s up to the AS’s policy. Much like the “openid” scope
>>> in OIDC, which switches on all sorts of contextual stuff in the request. So
>>> to handle something like this, an AS can easily declare that a given
>>> scope-style string or a given object property applies to different parts of
>>> the request. You don’t need to externalize it here.
>> I view the "openid" scope as an unfortunate design as OIDC was
>> constrained by building on top of OAuth. (a problem I hoped to avert by
>> having "identity" in scope for GNAP) The "openid" scope does not function
>> as scope per se, and I think it makes OIDC harder to understand as the
>> "openid" scope causes non-scope behavior.
>>> Do you have a concrete use case that requires that feature to be done in
>>> the way that you describe? I am trying to separate the driving use case
>>> from the proposed solutions to see what the differences are.
>> Perhaps the client wants access to be HIPPA compliant? The HIPPA
>> compliance signal applies to the scopes.
>> Adding other properties to an object is a well understood extension
>> mechanism. Adding an additional element to an array that does not act like
>> the other elements seems like a hack.
>>> Using JSON type polymorphism requires the AS to test each member of the
>>> array to determine if it is a string or an object. Only after detecting a
>>> RAR object does the AS know the client is making a RAR request.
>>> That’s correct — but the AS needs to parse the whole resources request
>>> in order to figure out what the client is asking for, anyway, whether it’s
>>> by strings or objects or whatever else might be defined by an extension. Is
>>> there an argument for having an AS do an early dispatch on a request before
>>> it’s fully parsed everything?
>> Let me clarify, the code is looking at the type of object that has been
>> parsed.
>> Determining if an item in an array is a scope or a RAR object by looking
>> at the type being either a string or an object seems less clear than a
>> property of an object explicitly declaring the type.
>>> This also limits the request to be composed only of scope strings or RAR
>>> objects. I don't see how other strings or objects could be used in the
>>> array, so there is no clear extension point in the "resources" array for
>>> other query mechanisms.
>>> That’s not the case in XYZ since we aren’t declaring that a string has
>>> to be an OAuth2-style scope. It can be, but ultimately it’s just a string
>>> that the AS can understand. And the objects are just that — objects. If the
>>> AS understands what the object is, it can be a RAR object or it can be
>>> something completely API-specific with another query language entirely.
>> But the other query language would need a type that has been reserved in
>> the RAR name space for there to be interop, so it effectively is a RAR
>> extension.
>> There are query languages in other domains that may not fit nicely into
>> RAR such as a query for medical records.
>> Yes, the medical records could be yet another top level property, but per
>> below, I think that is confusing.
>>> (Point, though: RAR already pretty much allows this by letting them be
>>> extended infinitely, a feature it inherits from XYZ)
>>> I’m proposing that we do the same thing with GNAP: it’s an array of
>>> strings or objects and each of those means the same thing, “something the
>>> client is asking for”.
>>> Just as RAR has a "type" property, I propose the "resources"
>>> ("authorizations" in XAuth) be an object, where the other properties are
>>> determined by the "type" property. This allows extensions to define new
>>> ways to query for an authorization rather than having to fit into scopes or
>>> RAR.
>>> It’s my stance that this is an unnecessary limitation at this level. The
>>> objects within the array should be typed, like RAR, but it doesn’t make
>>> sense for the overall request to be typed. Instead, there should be one
>>> “type" of query in the core, what XYZ calls the “resources” request. Other
>>> query languages should be added as extensions either to the RAR-style
>>> objects (by defining a type at that level) or as a separate top-level
>>> member.
>>> For example, let’s take the OIDC “claims” query language. My current
>>> thought is that this really shouldn’t be a part of the “resources” or
>>> “claims” part of the request, but instead as its own top-level member, like
>>> it is in the OIDC protocol today. The main reason for this is the nature of
>>> the OIDC claims language: it specifies targets for the resulting data, and
>>> those targets cross different ways to return things. So it doesn’t actually
>>> affect just resources like the UserInfo Endpoint, or the ID Token, but both
>>> and potentially others out there. If your system supported such an
>>> extension, it could theoretically forego both the built-in “claims” and
>>> “resources” parts of the request, and use the “oidc_claims_query” member
>>> (or whatever it would be called). This would let such an extension use the
>>> OIDC claims processing mechanism as it is today.
>>> To me, this remains much more understandable, extensible, and clean.
>> While this may be more understandable to a developer just porting an app
>> OIDC that only wants OIDC results, but I think it is more complicated as
>> soon as the developer wants other results, which is likely what prompted
>> the developer to use GNAP instead of ODIC.
>> I think it is easier to understand if all the claims are in one
>> container, and all the authorizations are in another container.
>> If a developer wants access to some resources, some claims, and an OpenID
>> Token, they are having to use "claims", "resources", and
>> "oidc_claims_query".  Now the claims and access tokens are spread across
>> multiple containers. There are some claims in the "claims" container, and
>> some "claims" in the "oidc_claims_query" container. And is the access token
>> response in "oidc_claims_query" the same as an access token response in
>> "resources"? It would seem simpler if they were, and if all the access
>> tokens came back in the same container.
>> Per Aaron's post that you have referred to, the developer can get sme
>> bare claims directly in the response in the "claims" object, an ID Token
>> that has the same or different claims, and if they want, an access token
>> that they can call a user_info endpoint to get the same, or different
>> claims.
>> For example, an enterprise app client may want an ID Token with the email
>> address, bare claims for the user's name and a URI to a profile photo, and
>> an access token to query which groups a user belongs to.
>> We are still re-using the OIDC claims, but we are not mixing claims and
>> authorizations.
>> /Dick
>> [1]
>> ᐧ
>> ᐧ