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

Dick Hardt <dick.hardt@gmail.com> Thu, 09 July 2020 19:30 UTC

Return-Path: <dick.hardt@gmail.com>
X-Original-To: txauth@ietfa.amsl.com
Delivered-To: txauth@ietfa.amsl.com
Received: from localhost (localhost [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id A3FEB3A0E43 for <txauth@ietfa.amsl.com>; Thu, 9 Jul 2020 12:30:21 -0700 (PDT)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -2.096
X-Spam-Level:
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: ietfa.amsl.com (amavisd-new); dkim=pass (2048-bit key) header.d=gmail.com
Received: from mail.ietf.org ([4.31.198.44]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 3iNruRFd82w1 for <txauth@ietfa.amsl.com>; Thu, 9 Jul 2020 12:30:18 -0700 (PDT)
Received: from mail-lj1-x234.google.com (mail-lj1-x234.google.com [IPv6:2a00:1450:4864:20::234]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ietfa.amsl.com (Postfix) with ESMTPS id DDA523A0E27 for <txauth@ietf.org>; Thu, 9 Jul 2020 12:30:17 -0700 (PDT)
Received: by mail-lj1-x234.google.com with SMTP id j11so3715189ljo.7 for <txauth@ietf.org>; Thu, 09 Jul 2020 12:30:17 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=gqVou8OQSNDhq0VbTaxicflhqEjhFNI3crixoDNy9Rk=; b=eb+pq88RPBPbTiHWsSmFPZLB7G8T6ItXwc6U69voWrJwVUmYY45ii3L7gQI2Zab46P RBl3OE3A8nVD3/0Gh7iAX3VW9oVctXy3yYwHrPKZVR6UxZmcFstjJHEPtWJyxPQCvfAw Ty7SfPH4tAEIj64gYTwC+O9fS/uzdZcqOcsOJICW8C3/2dCjtJAkr846RHgXWb8wxMx+ +DQugcYw55i/hfGLdL5Aqil2nonVV54uGhoyN2JOaRm2c6ivxGNdQyoSbrOXTF5OppAv UIl77D+4T2IlbR+b8+ko48YLGUUpHOe47a65N5sNGbnlC2ookK5aEY0NsKI5kbE9h0T2 Qdtg==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=gqVou8OQSNDhq0VbTaxicflhqEjhFNI3crixoDNy9Rk=; b=q3o16pHHWMgD8nCr6ISy8BOn9U0vjmrkjaPSXEbIXiAkmz24GJw/3ASDikeKP04P2M /tzLFLhyMtFVjHzCjgPga40r187fR6xG9mGsfFNkT856CTeyUmN4IJpybpMWPPXUtCD/ SBvp4iBhHnh1QWq0hlvo7c3CrOtgBpStyOAM+C0UPtJ4jzCB+h2vTa9iMG6DmnkfWJ2O 7kBl04BvjVaZzQyqrsyJpn4BVDBarvsTavwpuPNZQnNeuSBZFmQ38VQfwgFYiFLgvzsM GxVNrQFNpRgo+1INSJIFOtEwKRAKwGX3ncaeOFUgJWqDg5B4wzpP5aUX4JUyadJV65+W HlYQ==
X-Gm-Message-State: AOAM532+OzufsLgrjgF4eA6bB4GkVI2XxA8aJqdy08HUDTZN0jxWKgJZ qecI2jVh4MNPMfqL1NVQ7GZSg4Tf/nwICCvhXF4=
X-Google-Smtp-Source: ABdhPJwpeeO0KBOoA2fIvdd/4adcEScXhGb398HoavqAEWVZ5jqZvinT+L0BGpuDrdNMcuZC4HBVxYa/yitvV0SZ7AM=
X-Received: by 2002:a05:651c:547:: with SMTP id q7mr35482397ljp.437.1594323015584; Thu, 09 Jul 2020 12:30:15 -0700 (PDT)
MIME-Version: 1.0
References: <CAD9ie-vnA98pobbboS00SAHneEG52_8eMxh_sE3r3jg6gyooGg@mail.gmail.com> <E9EC90C9-7A9A-4909-8627-A161B33E941F@mit.edu> <CAD9ie-vyB8+5jS=K_qUHfvxsF2wPV5APRo+7WUDfJxNzJONJpg@mail.gmail.com> <8CC8B466-FD6F-4C23-8DAA-99B8A9BDF548@mit.edu> <CAD9ie-u9z7Mc-wNjztoOTy4N_Z9jFDc2Sb6quLspasMGAMKdSw@mail.gmail.com> <097FB93E-96DA-4DF6-8511-0B32FD321211@mit.edu> <CAD9ie-tpuisauOFGiUj65-RcYPtcvW_gZP1CAadqq5cE6P36HQ@mail.gmail.com> <EE4A7D91-1106-44CB-92BF-C3AA3649BDFE@mit.edu> <CAD9ie-saoc2FUm46r4h1B27iYK04j_skf5-zJR7EXLmWBzj=hA@mail.gmail.com> <F41A8F88-C1B4-4CE2-8573-7A03C086D25B@mit.edu> <CAD9ie-tHCg9Ti1xWuzUP5EGLAcU2cpFALErqq98+fPnD3enZCQ@mail.gmail.com> <820525FD-4556-4617-8D89-C600D8C90C33@mit.edu> <CAD9ie-uwz_Li7n9iuX_--YtWeE+HX5sWEe95nZ8Y0akYh8WTHg@mail.gmail.com> <A7E1F61B-78F8-4647-847A-E1C8909EA452@mit.edu> <CAD9ie-tSjEWT-+D43yKaFZmEdFcbL=fyM0kHuFX_fNa4zHdm1w@mail.gmail.com> <6D58464F-3EFA-4367-9033-91FCB9CF40AC@mit.edu>
In-Reply-To: <6D58464F-3EFA-4367-9033-91FCB9CF40AC@mit.edu>
From: Dick Hardt <dick.hardt@gmail.com>
Date: Thu, 9 Jul 2020 12:29:38 -0700
Message-ID: <CAD9ie-syw5YJVsJHncZ-PcLQbYC4r=4LLSQCKtMP=-hGKqT0SA@mail.gmail.com>
To: Justin Richer <jricher@mit.edu>
Cc: txauth@ietf.org
Content-Type: multipart/alternative; boundary="0000000000000052f605aa074161"
Archived-At: <https://mailarchive.ietf.org/arch/msg/txauth/Qd20NeNcZWI6crQsRYbKiOmskDg>
Subject: Re: [Txauth] Polymorphism (Was: JSON Schema?)
X-BeenThere: txauth@ietf.org
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: <txauth.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/txauth>, <mailto:txauth-request@ietf.org?subject=unsubscribe>
List-Archive: <https://mailarchive.ietf.org/arch/browse/txauth/>
List-Post: <mailto:txauth@ietf.org>
List-Help: <mailto:txauth-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/txauth>, <mailto:txauth-request@ietf.org?subject=subscribe>
X-List-Received-Date: Thu, 09 Jul 2020 19:30:22 -0000

Yes. Which is why I referred to Aaron's post originally which calls out for
returning plain text claims.

I don't really understand what you mean by "OIDC claims query language".
The claims mean the same thing. And I was also reusing the modifiers such
as {"essential": true}. What top level functionality are you referring to?

On Thu, Jul 9, 2020 at 12:21 PM Justin Richer <jricher@mit.edu> wrote:

> Ah, so you’re saying that the “userinfo” claims would be returned
> directly? I didn’t realize that you had intended to change how the OIDC
> claims query language functioned in your examples, and had assumed it would
> work the same was as the spec you were referencing. Your example of
> splitting it up makes that more clear. Though I would argue at that point,
> you’re creating a new query language since you’re not using the top-level
> functionality from ODIC’s definition.
>
>  — Justin
>
> On Jul 9, 2020, at 3:15 PM, Dick Hardt <dick.hardt@gmail.com> wrote:
>
> You are jumping to many conclusions below. Let me break down the proposal
> into some bite size chunks.
>
> The developer would like to get some plain text OIDC claims about the user:
>
>     "claims":{
>         "oidc": {
>             "userinfo" : {
>                 "name" : { "essential" : true },
>                 "photo" : { "essential" : false }
>             },
>
> The developer would like to get an OIDC ID Token:
>
>     "claims":{
>         "oidc": {
>             "id_token" : {
>                 "email"          : { "essential" : true },
>                 "email_verified" : { "essential" : true }
>             }
>     }
>
> The developer would like to get an access token to acquire OIDC claims:
>
> "authorizations": {
>         "type":"oidc",
>         "claims": ["name", "picture"]
>     }
>
> The developer would like to get an access token to access photos:
>
> "authorizations": {
>         "type":"oauth_scope",
>         "scope": "read_photos"*
>     }
>
> The developer would like to get some VC claims:
>
> "claims" {
>         "vc": {
>             "some_vc": "query_mechanism"
>         }
> }
>
> The developer would like to do all of the above at once:
>
> {
>     "authorizations": {
>         "userinfo": {
>             "type":"oidc",
>             "claims": ["name", "picture"]
>         },
>         "photos": {
>             "type":"oauth_scope",
>             "scope": "read_photos"
>         }
>     },
>     "claims":{
>         "oidc": {
>             "id_token" : {
>                 "email"          : { "essential" : true },
>                 "email_verified" : { "essential" : true }
>             },
>             "userinfo" : {
>                 "name" : { "essential" : true },
>                 "photo" : { "essential" : false }
>             }
>         },
>     "vc": {
>         "some_vc": "query_mechanism"
>         }
>     }
> }
>
> In the results, the developer gets claims back in the response claims
> object, and access tokens etc. back in the authorizations object. Just
> because an access token returns claims, it is still an access token.
>
> Yes, the developer can't get a single access token that can both acquire
> OIDC claims, and access photos (there are separate tokens for each), but I
> would argue that separating the access is a positive security property, as
> a client component accessing the user profile has a token independent of
> the client component accessing photos. And at the AS, there is no
> requirement for the userinfo endpoint to take the same token as the photo
> endpoint.
>
> Somewhat of a tangent, I'm thinking that
>
>     "claims":{
>         "oidc": {
>             "id_token" : {},
>             "userinfo" : {}
>         },
>
> is a little verbose, and:
>
>     "claims":{
>         "oidc_token": {},
>         "oidc_info": {}
>     },
>
> works better and makes it easier to distinguish support for ID tokens vs
> plain claims.
>
> wrt. my position on reusing OIDC -- it has not changed. I have viewed the
> OIDC claims as the "query language". No need to reinvent that. We are
> creating a new protocol, so no need to use the OAuth or OIDC protocols.
>
> * OAuth scopes could be a space separates string to be consistent with
> OAuth 2, or an array of strings so that it is more JSON like. I have no
> strong opinion.
>
> scope.split(' ').forEach()
>
> is not that much more complex than
>
> scope.forEach()
>
>
>
> On Thu, Jul 9, 2020 at 8:39 AM Justin Richer <jricher@mit.edu> wrote:
>
>> But this approach doesn’t keep things in their respective containers —
>> you’ve explicitly got “claims” underneath the “authorizations” header, and
>> you’ve got items that come back as rights associated with the access token
>> (which should be “authorizations”) in the “userinfo” section under the
>> “claims” header. And as far as I can tell, these two sections are redundant
>> to each other. Everything is everywhere.
>>
>> Additionally, this approach and syntax makes it difficult to combine
>> different kinds of requests into one. One of OpenID Connect’s biggest
>> draws, as I’m sure you recall, is that it could be combined with a request
>> for non-OIDC resources. This was the real innovation that Twitter and
>> Facebook’s identity APIs brought, access to more than just authentication,
>> and what Google had tried to replicate with the awkward OpenID 2 + OAuth 1
>> hybrid system. Taking a step back from the existing solutions of OpenID 2
>> and OAuth 1 let us, as the community, see the value in the pattern that
>> became OIDC on top of OAuth 2.
>>
>> Sure, you could say that the “oidc” type also can allow a “scopes”
>> parameter, but what about a RAR style object, then? And what about when
>> someone comes up with some new way to request access rights, or a VC-based
>> query language? Does every “type” need to now know about every other “type”
>> in order for an AS to be able to figure out how they go together? This
>> seems like the protocol definition limiting what combinations the AS can
>> handle, or what an RS might want to use.
>>
>> My stance is that GNAP should have a way to query for rights in the
>> access token (“authorizations” in xauth parlance) and identifiers for the
>> user (“claims” in xauth parlance), and anything else should be an extension
>> with potentially different models. The AS would process the
>> “authorizations” equivalent (for the access token) alongside any other
>> incoming query and then make a policy decision based on that.
>>
>> I find it interesting that you are now saying we don’t need to use the
>> OIDC request format when previously you’ve made it clear that you were in
>> favor of pointing at external query languages, including their syntax. I’m
>> glad to see that you’re now looking at things in a more flexible way, but I
>> think it’s important that we be careful and conscientious about how we
>> reference any external query languages in GNAP.
>>
>>  — Justin
>>
>> On Jul 8, 2020, at 6:55 PM, Dick Hardt <dick.hardt@gmail.com> 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 <jricher@mit.edu> 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 <dick.hardt@gmail.com> wrote:
>>>
>>>
>>>
>>> On Wed, Jul 8, 2020 at 1:02 PM Justin Richer <jricher@mit.edu> wrote:
>>>
>>>> On Jul 8, 2020, at 3:16 PM, Dick Hardt <dick.hardt@gmail.com> 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] https://aaronparecki.com/2019/07/18/17/adding-identity-to-xyz
>>> ᐧ
>>>
>>>
>>> ᐧ
>>
>>
>> ᐧ
>
>
> ᐧ