New in IdentityServer4: Resource-based Configuration

For RC4 we decided to re-design our configuration object model for resources (formerly known as scopes).

I know, I know – we are not supposed to make fundamental breaking changes once reaching the RC status – but hey – we kind of had our “DNX” moment, and realized that we either change this now – or never.

Why did we do that?
We spent the last couple of years explaining OpenID Connect and OAuth 2.0 based architectures to hundreds of students in training classes, attendees at conferences, fellow developers, and customers from all types of industries.

While most concepts are pretty clear and make total sense – scopes were the most confusing part for most people. The abstract nature of a scope as well as the fact that the term scope has a somewhat different meaning in OpenID Connect and OAuth 2.0, made this concept really hard to grasp.

Maybe it’s also partly our fault, that we stayed very close to the spec-speak with our object model and abstraction level, that we forced that concept onto every user of IdentityServer.

Long story short – every time I needed to explain scope, I said something like “A scope is a resource a client wants to access.”..and “there are two types of scopes: identity related and APIs…”.

This got us thinking if it would make more sense to introduce the notion of resources in IdentityServer, and get rid of scopes.

What did we do?
Before RC4 – our configuration object model had three main parts: users, client, and scopes (and there were two types of scopes – identity and resource – and some overlapping settings between them).

Starting with RC4 – the configuration model does not have scope anymore as a top-level concept, but rather identity resources and API resources.

terminology

We think this is a more natural way (and language) to model a typical token-based system.

From our new docs:

User
A user is a human that is using a registered client to access resources.

Client
A client is a piece of software that requests tokens from IdentityServer – either for authenticating a user (requesting an identity token)
or for accessing a resource (requesting an access token). A client must be first registered with IdentityServer before it can request tokens.

Resources
Resources are something you want to protect with IdentityServer – either identity data of your users (like user id, name, email..), or APIs.

Enough talk, show me the code!
Pre-RC4, you would have used a scope store to return a flat list of scopes. Now the new resource store deals with two different resource types: IdentityResource and ApiResource.

Let’s start with identity – standard scopes used to be defined like this:

public static IEnumerable<Scope> GetScopes()
{
    return new List<Scope>
    {
        StandardScopes.OpenId,
        StandardScopes.Profile
    };
}

..and now:

public static IEnumerable<IdentityResource> GetIdentityResources()
{
    return new List<IdentityResource>
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile()
    };
}

Not very different. Now let’s define a custom identity resource with associated claims:

var customerProfile = new IdentityResource(
    name:        "profile.customer",
    displayName: "Customer profile",
    claimTypes:  new[] { "name""status""location" });

This is all that’s needed for 90% of all identity resources you will ever define. If you need to tweak details, you can set various properties on the IdentityResource class.

Let’s have a look at the API resources. You used to define a resource-scope like this:

public static IEnumerable<Scope> GetScopes()
{
    return new List<Scope>
    {
        new Scope
        {
            Name = "api1",
            DisplayName = "My API #1",
 
            Type = ScopeType.Resource
        }
    };
}

..and the new way:

public static IEnumerable<ApiResource> GetApis()
{
    return new[]
    {
        new ApiResource("api1""My API #1")
    };
}

Again – for the simple case there is not a huge difference. The ApiResource object model starts to become more powerful when you have advanced requirements like APIs with multiple scopes (and maybe different claims based on the scope) and support for introspection, e.g.:

public static IEnumerable<ApiResource> GetApis()
{
    return new[]
    {
        new ApiResource
        {
            Name = "calendar",
 
            // secret for introspection endpoint
            ApiSecrets =
            {
                new Secret("secret".Sha256())
            },
 
            // claims to include in access token
            UserClaims =
            {
                JwtClaimTypes.Name,
                JwtClaimTypes.Email
            },
 
            // API has multiple scopes
            Scopes =
            {
                new Scope
                {
                    Name = "calendar.read_only",
                    DisplayName = "Read only access to the calendar"
                },
                new Scope
                {
                    Name = "calendar.full_access",
                    DisplayName = "Full access to the calendar",
                    Emphasize = true,
 
                    // include additional claim for that scope
                    UserClaims =
                    {
                        "status"
                    }
                }
            }
        }
    };

IOW – We reversed the configuration approach, and you now model APIs (which might have scopes) – and not scopes (that happen to represent an API).

We like the new model much better as it reflects how you architect a token-based system much better. We hope you like it too – and sorry for moving the cheese ;)

As always – give us feedback on the issue tracker. RTM is very close.

This entry was posted in .NET Security, ASP.NET, OAuth, Uncategorized, WebAPI. Bookmark the permalink.

44 Responses to New in IdentityServer4: Resource-based Configuration

  1. Great thinking… I appreciate you guys staying so close to the spec, but this is an excellent change! Great job with Identity Server!

  2. pierslawson says:

    I think the term “Client” is also confusing. In main domains the client is somebody who uses your software, i.e. the person (customer) who you sold a license to. I prefer the term “User Agent” which is generally thought of as something that is acting on behalf of a user.

    • Well – don’t agree.

      Client = software
      User = human

      • pierslawson says:

        I wasn’t seriously expecting it to change… I just remember it being something that confused me when I first started looking at identity mechanisms, especially after working on “RESTful” APIs where the term User Agent was often used. Having said that, reading round more, I see both terms being used with regard to OAuth and meaning different concepts… the Client being the software that uses the tokens to talk to say an API and the User Agent being the software (usually a browser) that is trusted to capture credentials from the user and provide them to the OAuth server. Does that sound right?

  3. The client is the software that requests tokens from the token service (a client to the token service). Some literature also uses the term “relying party”.

    A user-agent is typically a browser.

  4. lutandongqakaza says:

    I have spent the last months explaining to my colleagues what scopes are, and I still think that most of them struggle with the differences between a scope and a user claim since 99% of the time my peers have to interface with our identity provider (built ontop of ids4). This change is highly welcomed from my side since your api resource modelling seems more explicit and expressive in what it is.

    Unfortunately despite these welcomed changes. my colleagues they will probably confuse what scopes roles and claims are but I don’t blame them, it took me a while to get it too…

  5. Asif Mushtaq says:

    Scope might be confusing but I think that resource is also not less confusing.
    Firstly scopes covers all sort of operations to access any resources like read-friendlist and manage-friendlist, Secondly the term is more ubiquitous now and anyone who had work with any sort of Oauth/OpenId apis would understand it.

  6. Andrew Albright says:

    What will this mean for the clients – specifically those of us using your oidc-client javascript library to consume the services provided by Identity Server 4? Will we need to change anything?

  7. I like the change. I’m early in the development phase of our project so making the changes now is not as painful. I need to remodel our configuration database entities, which shouldn’t take too long.

  8. Eric Green says:

    So for the `AllowedScopes` on a Client. If it is an `ApiResource`, do you put the `Name` of the `ApiResource` as an `AllowedScope` or the name of a `Scope` defined on the `ApiResource` ?

    • Nothing has changed here – you put the one or more scopes of one or more resources.

      • Eric Green says:

        So if I add a scope from an ApiResouce to a client, will the ApiResource’s user claims be added to the access token, along with the user claims defined on the scope itself?

  9. stanstare says:

    This is an excellent choice at a key moment on the project – it all reads so much more clearly now that the “scopes” are not so ambiguous!

  10. markiano says:

    So in order to create an IdentityResource list (GetIdentityResources method above) we need to instantiate (use the new operator) each resource. What was the motivation of not defining static read-only properties for well known identity-related resources (like OpenId, Profile, etc) instead?

  11. RICHARD LEE DA SILVA SILVEIRA says:

    It will become a kind of OpenIDConnect.IdentityServer protocol?

  12. Why don’t ApiResource and IdentityResource share a common abstract class or interface? I’m finding in my implementation that having them not be covariant means I have to write a bit of code twice where it seems like once suffice. Obviously one can avoid the issue by using dynamic, but that’s undesirable.

  13. alessandra says:

    I have some questions regarding Identity Server and how does it work. And I can’t really find solutions to my questions anywhere. Could someone tell me how does it work? I mean, if I get clients from a database when initiating the server, if I add another one in the database, will it be able to recognize that client that moment? Or do I have to restart the server? How does InMemoryClients work?
    I would really appreciate if you could explain to me.

    Thank you very much

    • If you want to load your data from a database you would either write your own store implementation, or use our EF based implementation.

      See also here for further questions:
      https://identityserver4.readthedocs.io/en/release/intro/support.html

      • alessandra says:

        Ok, I know that I have to load info from the database….however, my question is: If I add manually another entry for clients in the database, will it be automatically added in the clients for IdentityServer, or do I have to restart the service? I am asking this because have multiple clients and they change all the time.

  14. If you load them from the database – they will always loaded from there. We support caching – but that is off by default.

    • alessandra says:

      Right now I am using an API to retrieve the clients from the database…and if I add a new client there, it doesn’t automatically loads the newly added client …it just checks the clients from the list that was populated when I started the server.

  15. alessandra says:

    So, using that with Identity Server 4, will load the clients every time?

  16. alessandra says:

    Hello,
    Thank you for your suggestions. It worked perfectly. Could you point me to proper documentation where I could find something where I don’t have to use in memory users? It would be of great help.

    Thank you

    • You need to authenticate the users yourself on the account controller. There are no in-memory users. Check the quickstart for asp.net identity.

      • alessandra says:

        I am trying to use an override for PasswordSignInAsync to check through an API the password validation, but I can’t seem to manage to login the user after checking that.

  17. As I said earlier – this is not a support channel. I already gave you the right link above.

  18. Dominic Purnell says:

    I find it confusing in the table structure of your EF sample that we have ClientScopes not say resources, also the code base uses the syntax AllowedScopes. Should this not be allowed resources? Am I missing the point here? Thanks.

  19. Chris says:

    Hi Dominick,
    I want to secure public API with IDSrv4.
    There are many client applications that gonna connect to my api (like integrators)

    How would you implement resource logic ?
    I was thinking about creating resource for every client :

    public static IEnumerable GetApis()
    {
    return new[]
    {
    new ApiResource
    {
    Name = “Client1.PublicApi”,

    // secret for introspection endpoint
    ApiSecrets =
    {
    new Secret(“secret”.Sha256())
    },

    // API has multiple scopes
    Scopes =
    {
    new Scope
    {
    Name = “api.read_only”,
    DisplayName = “Read only access to api”
    }
    }
    },

    new ApiResource
    {
    Name = “Client2.PublicApi”,

    // secret for introspection endpoint
    ApiSecrets =
    {
    new Secret(“secret”.Sha256())
    },

    // API has multiple scopes
    Scopes =
    {
    new Scope
    {
    Name = “api.full_access”,
    DisplayName = “Full access to api”
    }
    }
    },
    };

    • Hello —

      For non-commercial support, I recommend stackoverflow as there is a budding community around IdentityServer. For learning IdentityServer, we have our docs (which contains links to reading and videos). We also suggest reading the specs (which we also link to from our docs). Our issue tracker on github is intended for bugs, issues, and feature requests.

      If you are in a position where you or (better yet) your company would like commercial mentoring, training, architectural consulting, POC/project work, and/or production support, then email is the right place to start.

      Thanks.

  20. Joe says:

    Hello Dominick, I am wondering if there is a way to have dynamic client registration. In other words, if I wanted to store my client configurations in the persistence layer that could be dynamically queried. Thank you for your help, Joe

  21. Jay P says:

    Hi Dominick,

    Identity server 4 has recently been released. Could you please update this article to go with the latest release of the Identity Server 4?

    Thank you for the great work,
    Jay

Leave a comment