Skip to content
This repository was archived by the owner on Dec 13, 2018. It is now read-only.
This repository was archived by the owner on Dec 13, 2018. It is now read-only.

Discussion for Scope being a list and not a string? #614

Closed

Description

@brockallen

Was there ever any discussion as to why Scope on the OIDC middleware is a collection and not a string? ResponseType is still a string. It feels cumbersome. Anyone else feel the same way?

Activity

  1. kevinchalet commented on Dec 13, 2015

    @kevinchalet
    Contributor

    No discussion, but since I'm the guy who replaced the Scope property by a list, I can shed some light on the subject 😄

    The most important reason was for consistency: the OAuth2 generic middleware used a list of strings and the OIDC client middleware a single string. I opted for IList<string> because it was the most appropriate choice for the OAuth2 generic middleware, whose inheritors have to deal with non-standard implementations (Facebook, we hate you for using coma-separated scopes... 😄). Using a list hides how the scope is finally formatted by the specific implementation and makes the way users deal with scope consistent across the social providers.

  2. brockallen commented on Dec 14, 2015

    @brockallen
    Author

    Well, screw all of that, IMO. The katana OAuth2 MW is dead. And you know exactly what the OIDC and OAuth2 specs say for the scope. If the aberrant implementations needs something different, then do it in their own implementation.

    The job of frameworks is to make the life of the consumer easier, not the life of the implementer.

  3. kevinchalet commented on Dec 14, 2015

    @kevinchalet
    Contributor

    Well, screw all of that, IMO. The katana OAuth2 MW is dead.

    There was no OAuth2 generic middleware in Katana, so not sure to understand what you mean.

    And you know exactly what the OIDC and OAuth2 specs say for the scope.

    Both specs only state that scope is space-separated string, they don't mention anything about how we should implement it internally.

    If the aberrant implementations needs something different, then do it in their own implementation.

    The job of frameworks is to make the life of the consumer easier, not the life of the implementer.

    That's exactly what we do in the virtual FormatScope method. But it seems you totally missed my point. If we used a list, consumers would have to use different scopes patterns for Facebook and Google:

    options.Scope = "scope1,scope2"; // Facebook, Reddit.
    options.Scope = "scope1 scope2"; // Google (and the other standard providers)

    Using a list abstracts the way the scope is formatted:

    options.Scope.Add("scope1");
    options.Scope.Add("scope2");
  4. brockallen commented on Dec 14, 2015

    @brockallen
    Author

    There was no OAuth2 generic middleware in Katana, so not sure to understand what you mean.

    I was referring to the Katana OAuth2 authorization server middleware. Is that not what you mentioned above when you said that the point was for consistency? Perhaps we're not talking about the same thing.

    Other complaints:

    • It's not obvious what the defaults are (probably something that is AAD focused).
    • It's not consistent with the other settings (again, response_type)
    • Most people that use the OIDC middleware don't implement the OAuth2 katana middleware, so the consistency there is not obvious. (this is my comment above so perhaps we're not talking about the same thing)
    • It's not consistent with how it was in Katana. I already noticed another issue on the issue tracker asking why Scope was no longer the way it used to be.

    I know it's something small. But I teach this stuff to a lot of developers each year, and yet another little oddity like this requires yet another 5 minutes of explanation (or discovery on their part). Not time well spent (again for the consumer).

    Again, I'm sure this doesn't matter in the end. I'm just raising the point that when I'm using this MW as a consumer I'm pretty sure that 99% of the time I will need to write this code:

    options.Scope.Clear();
    options.Scope.Add("openid");
    options.Scope.Add("email");
    options.Scope.Add("profile");
    options.Scope.Add("api1");
    

    So I know exactly what I'm getting. Otherwise the code looks like:

    options.Scope.Add("email");
    options.Scope.Add("api1");
    

    And then i'm not really sure what I'm getting (or maybe the next dev on the project who inherits the code).

    So my argument here is for clarity and simplicity for the consuming developer. I felt the string based approach in the Katana OIDC middleware achieved that.

  5. kevinchalet commented on Dec 14, 2015

    @kevinchalet
    Contributor

    I was referring to the Katana OAuth2 authorization server middleware. Is that not what you mentioned above when you said that the point was for consistency? Perhaps we're not talking about the same thing.

    Hell no. By "OAuth2 generic middleware", I meant this thing. It's a whole new middleware, designed to support virtually any OAuth2 server. You can either subclass it or use the generic notifications. All the social providers inherit from OAuthMiddleware, including the 24 providers developed by the community.

  6. brockallen commented on Dec 14, 2015

    @brockallen
    Author

    As I said I already feel it's the aberrant implementation's fault if they don't follow the RFC/specs. This is the point of OIDC -- the fix the crap people did with OAuth2 and then called it authentication. But I digress.

    The reason for opening the issue stands: I think the design is more important for the consumer (for clarity and simplicity) rather than the implementer.

  7. kevinchalet commented on Dec 14, 2015

    @kevinchalet
    Contributor

    It's not consistent with the other settings (again, response_type)

    One could argue that response_type has only predefined (standard) sets that allow no customization (unlike scope, whose values are totally implementations-specific).

    It's not consistent with how it was in Katana. I already noticed another issue on the issue tracker asking why Scope was no longer the way it used to be.

    There are many things that have been updated in vNext. This change is relatively explicit IMHO.

    And then i'm not really sure what I'm getting (or maybe the next dev on the project who inherits the code). So my argument here is for clarity and simplicity for the consuming developer. I felt the string based approach in the Katana OIDC middleware achieved that.

    True. But on the other hand, it allows customers to only focus on the provider-specific scopes, without having to re-specify the standard ones. IMHO, it's clearer and simpler.

    You have valid points, but I don't think using a string is fundamentally better than using a list. I've reviewed ~20 providers for aspnet-contrib and many specify custom scopes in the options classes. Until now, I've never heard someone complain about the scope thingy (well, except you, obviously 😄).

  8. brockallen commented on Dec 14, 2015

    @brockallen
    Author

    Fair enough. I'd just like someone at Microsoft to spend some time thinking about it. I know it's late, and I know this is a nitpicky request, but I wanted some attention spent on it.

  9. Tratcher commented on Dec 14, 2015

    @Tratcher
    Member

    A) I prefer the List approach as it lets us worry about the formatting. It's even defined as a list in the spec https://tools.ietf.org/html/rfc6749#section-3.3.

    • The response_type comparison is interesting. It appears to be primarily single-value except for extensions. We only support code, id_token, and code id_token, and we use constants for those (OpenIdConnectResponseTypes).

    B) I agree that the defaults need to be more transparent.

  10. Tratcher commented on Dec 14, 2015

    @Tratcher
    Member

    We have two different ways to do defaults right now.

    1. Add them on the Options by default, then you need to clear them:
      https://github.com/aspnet/Security/blob/dev/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectOptions.cs#L163
      vs
    2. Only add them in the MW constructor if none were specified:
      https://github.com/aspnet/Security/blob/dev/src/Microsoft.AspNet.Authentication.Google/GoogleMiddleware.cs#L72-L77

    Which looks more useful?

    Either way we need better doc comments that explain the defaults. Google, MSA, and OIDC have defaults. Facebook doesn't. I don't think it's relevant to Twitter.

  11. kevinchalet commented on Dec 14, 2015

    @kevinchalet
    Contributor

    Personally, option 1) has always been my favorite approach (#257 (comment)).

  12. brockallen commented on Dec 14, 2015

    @brockallen
    Author

    The only default that makes sense to me is "openid" (since it's the OIDC MW). I don't think it matters where -- either in options ctor, or in MW ctor.

    Also, you should consider calling Distinct() on the scopes list before sending them to the OP -- currently they're duplicated in the authorization request if they're in the list multiple times. That's what apps will do when they don't know what the defaults are.

  13. changed the title [-]Discussion for Scope beign a list and not a string?[/-] [+]Discussion for Scope being a list and not a string?[/+] on Jan 14, 2016
  14. Eilon commented on Jan 14, 2016

    @Eilon
    Contributor

    For the defaults we should do (1) where the defaults are initialized in the list, and it's up to the app developer to add/remove/clear/whatever.

  15. added this to the 1.0.0-rc2 milestone on Jan 14, 2016
  16. kevinchalet commented on Jan 14, 2016

    @kevinchalet
    Contributor

    @Eilon now that "delegate-style" middleware extensions have all been removed, clearing/removing a property list is really ugly 😄

    Before:

    app.UseOpenIdConnectAuthentication(options => {
        options.Scope.Clear();
        options.Scope.Add("custom")
    });

    Now:

    var openIdConnectOptions = new OpenIdConnectOptions();
    openIdConnectOptions.Scope.Clear();
    openIdConnectOptions.Scope.Add("custom");
    app.UseOpenIdConnectAuthentication(openIdConnectOptions);
  17. smbecker commented on Jan 15, 2016

    @smbecker

    You could always add an extension method to simplify this case. Something like:

    public static class OpenIdConnectOptionsExtensions
    {
        public static OpenIdConnectOptions ResetScope(this OpenIdConnectOptions options, params string[] scopes) {
            options.Scope.Clear();
            foreach (var scope in scopes) {
                options.Scope.Add(scope);
            }
            return options;
        }
    }

    With usage:

    app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions().ResetScope("custom1", "custom2"));
  18. added 2 commits that reference this issue on Jan 27, 2016
  19. npnelson commented on May 19, 2016

    @npnelson

    For what it's worth, I unfortunately spent the last couple of days proving some of @brockallen 's points related to scopes and defaults. The default in the OpenIDConnectOptions includes "profile" which pulls everything (at least in the case of Auth0). Auth0 provides a lot of claims. Enough claims to overwhelm Safari mobile's cookie domain size limits, which results in an obscure CookieChunkingManagerError for users using mobile browsers.

    cookieerror

    The default of "profile" definitely burned me in this case and it took a lot of digging to figure out how to change the scopes. Changing the scopes is clumsy as @PinpointTownes pointed out (and not easily discoverable when you are trying to learn all the other new stuff).

    I am not sure if there is anything actionable because removing "profile" would have its own set of issues, but at the very least, I think others can learn from my mistake.

  20. brockallen commented on May 19, 2016

    @brockallen
    Author

    @leastprivilege and I just did a workshop this week on this topic and it was somewhat painful to do scopes as items in the collection. The API today favors the middleware implementer and not the consumer. Even now post-RC2 I think it'd still be nice to have scope as a single string.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions