Andrew
Andrew

Reputation: 404

custom properties within token

I found that OpenIddictToken has Properties and Payload fields. As I understand that can be populated by custom data which will be persisted in database but won't be send to client. But I can't find how should I write/read these properties? I saw that I can provide custom properties to AuthenticationTicket but it seems to be completely different properties. What is the right way to work with these properties?

Upvotes: 1

Views: 1284

Answers (2)

Andrew
Andrew

Reputation: 404

Really it looks like really weird behavior of OpenIddictAuthorizationManager specifically PopulateAsync function:

await Store.SetApplicationIdAsync(authorization, descriptor.ApplicationId, cancellationToken);
await Store.SetScopesAsync(authorization, ImmutableArray.CreateRange(descriptor.Scopes), cancellationToken);
await Store.SetStatusAsync(authorization, descriptor.Status, cancellationToken);
await Store.SetSubjectAsync(authorization, descriptor.Subject, cancellationToken);
await Store.SetTypeAsync(authorization, descriptor.Type, cancellationToken);

it populates everything except properties. So here what I did to persist properties in database. First I added properties I need in authorization phase /connect/authorize:

var ticket = new AuthenticationTicket(
  principal,
  new AuthenticationProperties(new Dictionary<string, string>{ { "name", "value" } }),
  OpenIddictServerDefaults.AuthenticationScheme);

then as suggested in an answer above I made my own tiny auth manager:

public class AuthorizationManager<TAuthorization>: OpenIddictAuthorizationManager<TAuthorization> where TAuthorization : OpenIddict.MongoDb.Models.OpenIddictAuthorization
    {
        public AuthorizationManager(...): base(cache, resolver, logger, options)
        {
        }

        public async override Task PopulateAsync(
            [NotNull] TAuthorization authorization,
            [NotNull] OpenIddictAuthorizationDescriptor descriptor,
            CancellationToken cancellationToken = default)
        {
            await base.PopulateAsync(authorization, descriptor, cancellationToken);
            if (descriptor.Properties.Any())
            {
                authorization.Properties = new MongoDB.Bson.BsonDocument(
                    descriptor.Properties.Where(p => p.Key[0] != '.') // skip scopes and other technical fields
                    .Select(p => new MongoDB.Bson.BsonElement(p.Key, p.Value))
                    );
            }
        }
    }

where I populated all the properties. And finally I just enabled my manager:

options.ReplaceAuthorizationManager<Services.OpenId.AuthorizationManager<OpenIddictAuthorization>>();

Upvotes: 0

K&#233;vin Chalet
K&#233;vin Chalet

Reputation: 42070

The Properties column - common to all OpenIddict entities - is indeed meant to be used as a generic bag for non-essential properties, which allows storing data without having to change the schema. Properties are not directly accessible using the default managers. Instead, you're encouraged to create your own manager derived from the built-in one and use Store.GetPropertiesAsync()/Store.SetPropertiesAsync() to access the properties.

OrchardCore's OpenID module has a custom application manager that uses it to store application roles so you can take a look at the source code to see how they are used: https://github.com/OrchardCMS/OrchardCore/blob/dev/src/OrchardCore/OrchardCore.OpenId.Core/Services/Managers/OpenIdApplicationManager.cs#L86-L92

Note: in OpenIddict 3.0, Store.GetPropertiesAsync()/Store.SetPropertiesAsync() were updated to use System.Text.Json instead of JSON.NET.


The Payload column is used to store reference tokens and is not meant to be used to store additional data.


It's worth noting that there's a simple option if you want to store data that will be kept private: if you don't add the access token/identity token destinations to your private claims, they won't be persisted in access/identity tokens and will only be present in the authorization codes/refresh tokens, that are always encrypted and can't be read by the client.

Upvotes: 1

Related Questions