cmd-johnson / deno-oauth2-client

Minimalistic OAuth 2.0 client for Deno.
MIT License
45 stars 9 forks source link

Scope is not always a string #26

Open nZac opened 1 year ago

nZac commented 1 year ago

When trying to work with Linear OAuth I found this check. Linear returns a list back, not a string and thus the parse fails.

I'm not clear on need to check for scope at this stage, but maybe consider supporting a list of strings?

cmd-johnson commented 1 year ago

Hi there!

Looks to me like the Linear API doesn't properly follow the OAuth2 specification.

For one, Linear expects the scope parameter to contain comma-separated scopes, when the OAuth2 spec asks for space-separated scopes. That means that you'll probably run into issues when using the authorization code grant like getAuthorizationUri({ scope: ['scopeA', 'scopeB'], as oauth2_client will follow the spec and join those scopes with a space character. This part is easily circumvented by joining the scopes yourself using , before passing them to getAuthorizationUri.

The OAuth2 spec also expects the access token response's scope value to follow the same space-separated format as above.

Right now this module doesn't support any other formats and to keep things simple (and spec-compliant) I'd rather keep it that way by default. What would be possible is to allow users of the module to customize/override these checks in cases like these where the authorization server doesn't follow the spec.

Do you think that would be a good solution for this issue?

nZac commented 1 year ago

Yea, that would be a reasonable outcome. Someway to customize the behavior for misbehaving servers.

cmd-johnson commented 1 year ago

I'm now working on extending this module to also support OpenID Connect out of the box. The proposed changes also open up the OAuth2 Grant classes for extension, making it possible to override the validation behaviour of the default AuthorizationCodeGrant to account for things like non-spec conformant scope values.

If you want to try it out, you can import the WIP version from https://raw.githubusercontent.com/cmd-johnson/deno-oauth2-client/feature/oidc/mod.ts!

You'd probably have to create a new class like this, extending the AuthorizationCodeGrant:

class LinearAuthorizationCodeGrant extends AuthorizationCodeGrant {
  protected async parseTokenResponse(
    response: Response,
  ): Promise<
    { tokens: Tokens; body: AccessTokenResponse & Record<string, unknown> }
  > {
    // copy the validation logic from https://github.com/cmd-johnson/deno-oauth2-client/blob/feature/oidc/src/authorization_code_grant.ts#L156-L211 and modify the scope validation logic
  }
}

After that you should be able to use it like this:

const linearAuthorizationCodeGrant = new LinearAuthorizationCodeGrant({
  /* the same config as when calling new OAuth2Client() /*
});
// now use the linearAuthorizationCodeGrant instance like you use oauith2Client.code