Search code examples
javagoogle-signinstormpath

Stormpath can't retrieve account object for google sign-in


I have absolutely followed the google guide to the letters and the stormpath guide for integrating with google also to the letters

Now, a User can sign in successfully with Google, but Stormpath throws an exception on this line when attempting to retrieve a Stormpath Account object using the authorization code obtained from google;

ProviderAccountResult result = application.getAccount(request);

Here's the code snippet;

public static Account getAccount(String codeOrToken, AuthType authType){
    try{
        if(getDirectory() != null){
            ProviderAccountRequest request = null;
            switch(authType){
                case AUTH_CODE: 
                    request = Providers.GOOGLE.account().setCode(codeOrToken).build(); 
                    break;
                case ACCESS_TOKEN: 
                    request = Providers.GOOGLE.account().setAccessToken(codeOrToken).build(); 
                    break;
                default: 
                    break;
            }
            Application application = AuthUtil.getApplication();
            ProviderAccountResult result = application.getAccount(request);
            Account account = result.getAccount();
            account.getCustomData().put("isNew", result.isNewAccount());
            return account;
        }
    }catch(Exception ex){
        ex.printStackTrace();
    }
    return null;
}

And here's the Exception StackTrace;

16:45:02,170 ERROR [stderr] (http-localhost-127.0.0.1-8080-5) com.stormpath.sdk.resource.ResourceException: HTTP 400, Stormpath 7200 (http://docs.stormpath.com/errors/7200): Stormpath was not able to complete the request to Google: this can be caused by either a bad Google directory configuration, or the provided account credentials are not valid. Google error message: 400 Bad Request
16:45:02,172 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at com.stormpath.sdk.impl.ds.DefaultDataStore.execute(DefaultDataStore.java:492)

16:45:02,173 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at com.stormpath.sdk.impl.ds.DefaultDataStore.access$000(DefaultDataStore.java:67)

16:45:02,174 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at com.stormpath.sdk.impl.ds.DefaultDataStore$2.filter(DefaultDataStore.java:390)

16:45:02,175 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at com.stormpath.sdk.impl.ds.DefaultFilterChain.filter(DefaultFilterChain.java:47)

16:45:02,176 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at com.stormpath.sdk.impl.ds.ProviderAccountResultFilter.filter(ProviderAccountResultFilter.java:31)

16:45:02,177 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at com.stormpath.sdk.impl.ds.DefaultFilterChain.filter(DefaultFilterChain.java:52)

16:45:02,178 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at com.stormpath.sdk.impl.ds.api.ApiKeyQueryFilter.filter(ApiKeyQueryFilter.java:74)

16:45:02,180 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at com.stormpath.sdk.impl.ds.DefaultFilterChain.filter(DefaultFilterChain.java:52)

16:45:02,181 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at com.stormpath.sdk.impl.ds.cache.WriteCacheFilter.filter(WriteCacheFilter.java:80)

16:45:02,184 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at com.stormpath.sdk.impl.ds.DefaultFilterChain.filter(DefaultFilterChain.java:52)

16:45:02,184 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at com.stormpath.sdk.impl.ds.cache.ReadCacheFilter.filter(ReadCacheFilter.java:62)

16:45:02,185 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at com.stormpath.sdk.impl.ds.DefaultFilterChain.filter(DefaultFilterChain.java:52)

16:45:02,186 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at com.stormpath.sdk.impl.ds.api.DecryptApiKeySecretFilter.filter(DecryptApiKeySecretFilter.java:62)

16:45:02,187 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at com.stormpath.sdk.impl.ds.DefaultFilterChain.filter(DefaultFilterChain.java:52)

16:45:02,188 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at com.stormpath.sdk.impl.ds.EnlistmentFilter.filter(EnlistmentFilter.java:42)

16:45:02,189 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at com.stormpath.sdk.impl.ds.DefaultFilterChain.filter(DefaultFilterChain.java:52)

16:45:02,189 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at com.stormpath.sdk.impl.ds.DefaultDataStore.save(DefaultDataStore.java:411)

16:45:02,190 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at com.stormpath.sdk.impl.ds.DefaultDataStore.create(DefaultDataStore.java:322)

16:45:02,191 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at com.stormpath.sdk.impl.provider.ProviderAccountResolver.resolveProviderAccount(ProviderAccountResolver.java:46)

16:45:02,192 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at com.stormpath.sdk.impl.application.DefaultApplication.getAccount(DefaultApplication.java:325)

16:45:02,193 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at ng.ajo.socials.Google.getAccount(Google.java:79)

16:45:02,193 ERROR [stderr] (http-localhost-127.0.0.1-8080-5)   at ng.ajo.server.SocialsServlet.doPost(SocialsServlet.java:81)

//... The rest omitted for brevity

Please what could I be doing wrong and what could be the solution to this problem?

EDIT:

If this helps provide more context, here's how I create my Google Directory :

public class Google {

    public static Directory getDirectory(){
        try{
            Directory directory = DirectoryUtil.getGoogleDirectory();

            //if the google directory does not exist CREATE it
            if(directory == null){
                Client client = AuthUtil.getClient();
                directory = client.instantiate(Directory.class);
                directory.setName(dirName);
                directory.setDescription(dirDescription);

                CreateDirectoryRequest request = Directories.newCreateRequestFor(directory)
                            .forProvider(Providers.GOOGLE.builder()
                                    .setClientId(appID)
                                    .setClientSecret(appSecret)
                                    .setRedirectUri(redirectURI)
                                    .build()
                            ).build();

                Tenant tenant = client.getCurrentTenant();
                directory = tenant.createDirectory(request);
                Application application = AuthUtil.getApplication();
                application.addAccountStore(directory.getHref());
            }

            return directory;

        }catch(Exception ex){
            ex.printStackTrace();
        }
    }
}

And when I login to my Stormpath web console I can see that the directory has been created and indeed exists with all the configs in place... now, this whole thing should work, yet it doesn't... still leaves me confused!


Solution

    1. Go to https://console.developers.google.com/apis/credentials
    2. Be sure you "Add Credentials"
    3. Take note of "Client ID" and "Client secret"
    4. Add some "Authorized redirect URIs" like http://localhost:8080/googleOauthCallback
    5. Login at https://api.stormpath.com
    6. Add a Google Directory providing the proper ID, Secret and Redirect URI
    7. Now, let's obtain a Google code in a simple way (without having to create a web project). Open this URL in your browser; when prompted, select the Gmail account you want to use.

      https://accounts.google.com/o/oauth2/auth?client_id=XXXXXXX
      &response_type=code
      &scope=openid%20email
      &redirect_uri=http://localhost:8080/googleOauthCallback
      

      Note: XXXXXXX must be replaced by the Client ID you obtained in step 3.

    8. You will get a cannot connect to server error but this is OK as we are not running a Web Application where Google can call us back. The important thing here is in the URL. Just copy the code value. It will be something like 4/tcHrwq4N1eah1rwotyCEaXq-yfxBOYrIAVe2_ouHTMQ

    9. This code will retrieve the Google Account via Stormpath:

      Client client = Clients.builder().build();
      
      Application application = client.getResource(applicationHref, Application.class);
      
      ProviderAccountRequest request = Providers.GOOGLE.account()
              .setCode(code) //where code is the value we obtained in step 8
              .build();
      
      ProviderAccountResult result = application.getAccount(request);
      System.out.println("Account Email: " + result.getAccount().getEmail());
      ProviderData providerData = result.getAccount().getProviderData();
      System.out.println("Access Token: " + ((GoogleProviderData)providerData).getAccessToken());
      

    That's all...