javaspringspring-oauth2coinbase-api

How do I correctly get Coinbase OAuth2 user attributes on login with Spring?


To create a ClientRegistration.Builder for Coinbase, I'm using this code:

ClientRegistration.Builder builder = getBuilder(registrationId,
  ClientAuthenticationMethod.CLIENT_SECRET_BASIC, redirectUri)//
    .authorizationUri(CB_AUTHORIZATION_URI)//
    .tokenUri(CB_TOKEN_URI)//
    .userInfoUri(CB_USER_INFO_URI)//
    .userNameAttributeName(CB_USER_NAME_ATTRIBUTE_NAME)//
    .clientName(CB_CLIENT_NAME);

where CB_USER_INFO_URI = "https://api.coinbase.com/v2/user" (See step 4 in https://docs.cdp.coinbase.com/sign-in-with-coinbase/docs/sign-in-with-coinbase-integration). The problem is that the call does not return the user attributes directly. It wraps them in another map, and you have to use the "data" key in that map to get to the real user attributes. Also, I'm having to temporarily set CB_USER_NAME_ATTRIBUTE_NAME = "data" to get things to run.

Needless to say, this is not ideal because the user attributes are not being assigned properly. How do I solve this? Is there a way to get CB_USER_INFO_URI to return the attributes directly? It doesn't seem like it. The only other way out of this that I can think of is to subclass DefaultOAuth2UserService so that I can change two lines and then use it in defining my securityFilterChain like so:

        .userInfoEndpoint(uie -> uie.userService(userService))

Is there a better way? Am I just missing something?


Solution

  • No, you're not missing anything.

    According to the doc you linked, Coinbase complies with OAuth2, but not with OIDC => you have to make an extra call to user-info endpoint (can't use an ID token delivered with access & refresh ones).

    The default OAuth2UserService is configurable to some extent, but regarding username and authorities mapping, it is limited to claims at root level. As Coinbase provides it as nested claims, you have to write your own and configure it as you already found. Note that you can extend DefaultOAuth2UserService, but you don't have to: you might as well implement OAuth2UserService<OAuth2UserRequest, OAuth2User> from scratch, optionally using a DefaultOAuth2UserService as delegate.