无法在Cross Client谷歌oauth2.0中交换访问令牌和刷新令牌的授权代码 [英] Unable to exchange authorization code for access token and refresh token in Cross Client google oauth2.0

查看:67
本文介绍了无法在Cross Client谷歌oauth2.0中交换访问令牌和刷新令牌的授权代码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在在Android应用上实现Google Play服务登录并将授权代码传递到后端服务器时遇到了问题,因此服务器将交换访问令牌和刷新令牌的代码.

I've been having problems implementing Google Play Services login on my android app and passing the authorisation code to my backend server, so the server will exchange the code for access token and refresh token.

首先让我写几行已经尝试/阅读的内容:

First let me write a few lines what has already been tried/read:

在code.google.com/apis/console上,我创建了一个新项目,其中包含两个客户端(WEB客户端和Android安装的客户端) 阅读 https://developers.google.com/+/上的文章mobile/android/sign-in#cross-platform_single_sign_on

on code.google.com/apis/console I've created a new project, with two clients (WEB client and Android installed client) read articles onhttps://developers.google.com/+/mobile/android/sign-in#cross-platform_single_sign_on and http://android-developers.blogspot.com/2013/01/verifying-back-end-calls-from-android.html

这是我的客户端代码,用于检索授权代码和IdToken:

Here is my code for client side that retrieves the authorization code and IdToken:

    package com.google.drive.samples.crossclientoauth2;

    import java.util.Arrays;
    import java.util.List;

    import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.widget.EditText;

import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.UserRecoverableAuthException;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;

public class MainActivity extends Activity {

  final private String CLIENT_ID = MY WEB SERVER'S CLIENT ID;
  final private List<String> SCOPES = Arrays.asList(new String[]{
      "https://www.googleapis.com/auth/plus.login",
      "https://www.googleapis.com/auth/drive",
      "https://www.googleapis.com/auth/youtube",
      "https://www.googleapis.com/auth/youtube.readonly"
  });

  // I have modified the above line of code.  

  private GoogleAccountCredential mCredential;

  private EditText mExchangeCodeEditText;
  private EditText mIdTokenEditText;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);
    mExchangeCodeEditText = (EditText) findViewById(R.id.editTextExchangeCode);
    mIdTokenEditText = (EditText) findViewById(R.id.editTextIdToken);

    // initiate a credential object with drive and plus.login scopes
    // cross identity is only available for tokens retrieved with plus.login
    mCredential = GoogleAccountCredential.usingOAuth2(this, null);

    // user needs to select an account, start account picker
    startActivityForResult(
        mCredential.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER);

  }

  /**
   * Handles the callbacks from result returning
   * account picker and permission requester activities.
   */
  @Override
  protected void onActivityResult(
      final int requestCode, final int resultCode, final Intent data) {
    switch (requestCode) {
    // user has  returned back from the account picker,
    // initiate the rest of the flow with the account he/she has chosen.
    case REQUEST_ACCOUNT_PICKER:
      String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
      if (accountName != null) {
        mCredential.setSelectedAccountName(accountName);
        new RetrieveExchangeCodeAsyncTask().execute();
        new RetrieveJwtAsyncTask().execute();
      }
      break;
    // user has returned back from the permissions screen,
    // if he/she has given enough permissions, retry the the request.
    case REQUEST_AUTHORIZATION:
      if (resultCode == Activity.RESULT_OK) {
        // replay the same operations
        new RetrieveExchangeCodeAsyncTask().execute();
        new RetrieveJwtAsyncTask().execute();
      }
      break;
    }
  }

  /**
   * Retrieves the exchange code to be sent to the
   * server-side component of the app.
   */

  public class RetrieveExchangeCodeAsyncTask
      extends AsyncTask<Void, Boolean, String> {


    @Override
    protected String doInBackground(Void... params) {
      String scope = String.format("oauth2:server:client_id:%s:api_scope:%s",
          CLIENT_ID, TextUtils.join(" ", SCOPES));
      try {
        return GoogleAuthUtil.getToken(
            MainActivity.this, mCredential.getSelectedAccountName(), scope);
      } catch (UserRecoverableAuthException e) {
        startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION);
      } catch (Exception e) {
        e.printStackTrace(); // TODO: handle the exception
      }
      return null;
    }

    @Override
    protected void onPostExecute(String code) {
      // exchange code with server-side to retrieve an additional
      // access token on the server-side.
        Log.v("first One ","code 1 is: "+ code);
      mExchangeCodeEditText.setText(code);
    }
  }

  /**
   * Retrieves a JWT to identify the user without the
   * regular client-side authorization flow. The jwt payload needs to be
   * sent to the server-side component.
   */
  public class RetrieveJwtAsyncTask
      extends AsyncTask<Void, Boolean, String> {

    @Override
    protected String doInBackground(Void... params) {
      String scope = "audience:server:client_id:" + CLIENT_ID;
      try {
        return GoogleAuthUtil.getToken(
            MainActivity.this, mCredential.getSelectedAccountName(), scope);
      } catch(UserRecoverableAuthIOException e) {
        startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION);
      } catch (Exception e) {
        e.printStackTrace(); // TODO: handle the exception
      }
      return null;
    }

    @Override
    protected void onPostExecute(String idToken) {
      // exchange encrypted idToken with server-side to identify the user
        Log.v("Second One","2222"+ idToken);
      mIdTokenEditText.setText(idToken);
    }
  }

  private static final int REQUEST_ACCOUNT_PICKER = 100;
  private static final int REQUEST_AUTHORIZATION = 200;

}

上面的代码给了我两个代码: 1. RetrieveExchangeCodeAsyncTask返回的一个-命名代码. 2.由RetrieveJwtAsyncTask类(名为IdToken)返回的第二个.

The above code gives me two codes: 1.One returned by RetrieveExchangeCodeAsyncTask - named code. 2.Second returned by RetrieveJwtAsyncTask class- named IdToken.

现在,首先我很困惑我需要从上面的哪个发送到要交换它的Web服务器. 我尝试在服务器端使用第一个(以"4/...."开头的那个)进行交换,但出现了空指针异常. 另外,请指定我需要使用的重定向URI.

Now in the first place i am confused as to which one from the above do i need to send to my web server where it will be exchanged. I tried using the first one(the one that starts as "4/....") for exchange at my server side but got A null pointer exception. Also please specify what redirect URI i need to use.

这是我用于交换的服务器端代码:

Here is my server-side code for exchange:

package com.myAuthSample.tial;

import java.io.IOException;

import com.myAuthSample.tial.MyClass.CodeExchangeException;
import com.myAuthSample.tial.MyClass.NoRefreshTokenException;

public class MyMainDemo {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
          try {
            MyClass.getCredentials("4/...something...", "state");  //passed the retrieved authorization code
        } catch (CodeExchangeException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoRefreshTokenException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}


public class MyClass {


    // Path to client_secrets.json which should contain a JSON document such as:
      //   {
      //     "web": {
      //       "client_id": "[[YOUR_CLIENT_ID]]",
      //       "client_secret": "[[YOUR_CLIENT_SECRET]]",
      //       "auth_uri": "https://accounts.google.com/o/oauth2/auth",
      //       "token_uri": "https://accounts.google.com/o/oauth2/token"
      //     }
      //   }
      private static final String CLIENTSECRETS_LOCATION = "/client_secrets_2.json";// client secrets of my android client

      // private static final String REDIRECT_URI = "<YOUR_REGISTERED_REDIRECT_URI>";

      private static String REDIRECT_URI ="";



      private static final List<String> SCOPES = Arrays.asList(
              "https://www.googleapis.com/auth/plus.login",
              "https://www.googleapis.com/auth/youtube");

      private static GoogleAuthorizationCodeFlow flow = null;

      /**
       * Exception thrown when an error occurred while retrieving credentials.
       */
      public static class GetCredentialsException extends Exception {

        protected String authorizationUrl;

        /**
         * Construct a GetCredentialsException.
         *
         * @param authorizationUrl The authorization URL to redirect the user to.
         */
        public GetCredentialsException(String authorizationUrl) {
          this.authorizationUrl = authorizationUrl;
        }

        /**
         * Set the authorization URL.
         */
        public void setAuthorizationUrl(String authorizationUrl) {
          this.authorizationUrl = authorizationUrl;
        }

        /**
         * @return the authorizationUrl
         */
        public String getAuthorizationUrl() {
          return authorizationUrl;
        }
      }

      /**
       * Exception thrown when a code exchange has failed.
       */
      public static class CodeExchangeException extends GetCredentialsException {

        /**
         * Construct a CodeExchangeException.
         *
         * @param authorizationUrl The authorization URL to redirect the user to.
         */
        public CodeExchangeException(String authorizationUrl) {
          super(authorizationUrl);
        }

      }

      /**
       * Exception thrown when no refresh token has been found.
       */
      public static class NoRefreshTokenException extends GetCredentialsException {

        /**
         * Construct a NoRefreshTokenException.
         *
         * @param authorizationUrl The authorization URL to redirect the user to.
         */
        public NoRefreshTokenException(String authorizationUrl) {
          super(authorizationUrl);
        }

      }

      /**
       * Exception thrown when no user ID could be retrieved.
       */
      private static class NoUserIdException extends Exception {
      }

      /**
       * Retrieved stored credentials for the provided user ID.
       *
       * @param userId User's ID.
       * @return Stored Credential if found, {@code null} otherwise.
       */
      static Credential getStoredCredentials(String userId) {
        // TODO: Implement this method to work with your database. Instantiate a new
        // Credential instance with stored accessToken and refreshToken.
        throw new UnsupportedOperationException();
      }

      /**
       * Store OAuth 2.0 credentials in the application's database.
       *
       * @param userId User's ID.
       * @param credentials The OAuth 2.0 credentials to store.
       */
      static void storeCredentials(String userId, Credential credentials) {
        // TODO: Implement this method to work with your database.
        // Store the credentials.getAccessToken() and credentials.getRefreshToken()
        // string values in your database.
          System.out.println("credentials are :    " + credentials.toString());
        throw new UnsupportedOperationException();
      }

      /**
       * Build an authorization flow and store it as a static class attribute.
       *
       * @return GoogleAuthorizationCodeFlow instance.
       * @throws IOException Unable to load client_secrets.json.
       */
      static GoogleAuthorizationCodeFlow getFlow() throws IOException {
        if (flow == null) {
          HttpTransport httpTransport = new NetHttpTransport();
          JacksonFactory jsonFactory = new JacksonFactory();    //...this was the original line....
        //  JsonFactory jsonFactory = new JacksonFactory();

         //my code....        
          Reader clientSecretReader = new InputStreamReader(MyClass.class.getResourceAsStream(CLIENTSECRETS_LOCATION));

          GoogleClientSecrets clientSecrets =
                  GoogleClientSecrets.load(jsonFactory,clientSecretReader);

          REDIRECT_URI =clientSecrets.getDetails().getRedirectUris().get(0);
          // my code ends...

    /*      GoogleClientSecrets clientSecrets =
              GoogleClientSecrets.load(jsonFactory,
                  MyClass.class.getResourceAsStream(CLIENTSECRETS_LOCATION));
      */
          flow =
              new GoogleAuthorizationCodeFlow.Builder(httpTransport, jsonFactory, clientSecrets, SCOPES)
                  .setAccessType("offline").setApprovalPrompt("force").build();
        }
        return flow;
      }

      /**
       * Exchange an authorization code for OAuth 2.0 credentials.
       *
       * @param authorizationCode Authorization code to exchange for OAuth 2.0
       *        credentials.
       * @return OAuth 2.0 credentials.
       * @throws CodeExchangeException An error occurred.
       */
      static Credential exchangeCode(String authorizationCode)
          throws CodeExchangeException {
        try {
          GoogleAuthorizationCodeFlow flow = getFlow();
          GoogleTokenResponse response =
              flow.newTokenRequest(authorizationCode).setRedirectUri(REDIRECT_URI).execute();
          return flow.createAndStoreCredential(response, null);
        } catch (IOException e) {
          System.err.println("An error occurred: " + e);
          throw new CodeExchangeException(null);
        }
      }

      /**
       * Send a request to the UserInfo API to retrieve the user's information.
       *
       * @param credentials OAuth 2.0 credentials to authorize the request.
       * @return User's information.
       * @throws NoUserIdException An error occurred.
       */
      static Userinfo getUserInfo(Credential credentials)
          throws NoUserIdException {
        Oauth2 userInfoService =
            new Oauth2.Builder(new NetHttpTransport(), new JacksonFactory(), credentials).build();
        Userinfo userInfo = null;
        try {
          userInfo = userInfoService.userinfo().get().execute();
        } catch (IOException e) {
          System.err.println("An error occurred: " + e);
        }
        if (userInfo != null && userInfo.getId() != null) {
          return userInfo;
        } else {
          throw new NoUserIdException();
        }
      }

      /**
       * Retrieve the authorization URL.
       *
       * @param emailAddress User's e-mail address.
       * @param state State for the authorization URL.
       * @return Authorization URL to redirect the user to.
       * @throws IOException Unable to load client_secrets.json.
       */
      public static String getAuthorizationUrl(String emailAddress, String state) throws IOException {
        GoogleAuthorizationCodeRequestUrl urlBuilder =
            getFlow().newAuthorizationUrl().setRedirectUri(REDIRECT_URI).setState(state);
        urlBuilder.set("user_id", emailAddress);
        return urlBuilder.build();
      }

      /**
       * Retrieve credentials using the provided authorization code.
       *
       * This function exchanges the authorization code for an access token and
       * queries the UserInfo API to retrieve the user's e-mail address. If a
       * refresh token has been retrieved along with an access token, it is stored
       * in the application database using the user's e-mail address as key. If no
       * refresh token has been retrieved, the function checks in the application
       * database for one and returns it if found or throws a NoRefreshTokenException
       * with the authorization URL to redirect the user to.
       *
       * @param authorizationCode Authorization code to use to retrieve an access
       *        token.
       * @param state State to set to the authorization URL in case of error.
       * @return OAuth 2.0 credentials instance containing an access and refresh
       *         token.
       * @throws NoRefreshTokenException No refresh token could be retrieved from
       *         the available sources.
       * @throws IOException Unable to load client_secrets.json.
       */
      public static Credential getCredentials(String authorizationCode, String state)
          throws CodeExchangeException, NoRefreshTokenException, IOException {
        String emailAddress = "";
        try {
          Credential credentials = exchangeCode(authorizationCode);
          Userinfo userInfo = getUserInfo(credentials);
          String userId = userInfo.getId();
          emailAddress = userInfo.getEmail();
          if (credentials.getRefreshToken() != null) {
            storeCredentials(userId, credentials);
            return credentials;
          } else {
            credentials = getStoredCredentials(userId);
            if (credentials != null && credentials.getRefreshToken() != null) {
              return credentials;
            }
          }
        } catch (CodeExchangeException e) {
          e.printStackTrace();
          // Drive apps should try to retrieve the user and credentials for the current
          // session.
          // If none is available, redirect the user to the authorization URL.
          e.setAuthorizationUrl(getAuthorizationUrl(emailAddress, state));
          throw e;
        } catch (NoUserIdException e) {
          e.printStackTrace();
        }
        // No refresh token has been retrieved.
        String authorizationUrl = getAuthorizationUrl(emailAddress, state);
        throw new NoRefreshTokenException(authorizationUrl);
      }


}

此外,我是否在我的服务器端代码(在MyClass中)中传递了正确的client_secret.json文件-这是android客户端.

Also,am i passing the correct client_secret.json file in my server side code(in MyClass)--which is of android client.

请帮助!!! 提前感谢.

Please help!!! Thanx in advance.

推荐答案

在此使用:

import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson.JacksonFactory;


private final val TRANSPORT: HttpTransport = new NetHttpTransport()
    private final val JSON_FACTORY: JacksonFactory = new JacksonFactory()


GoogleTokenResponse tokenResponse = new GoogleAuthorizationCodeTokenRequest(TRANSPORT, JSON_FACTORY,
                CLIENT_ID, CLIENT_SECRET, code, "postmessage").execute();

GoogleIdToken idToken = tokenResponse.parseIdToken();
String gplusId = idToken.getPayload().getSubject();

您必须使用上述相关变量替换client_id,client_secret和代码的值.

you must replace your values of the client_id,client_secret and code with above related variables.

更多信息在以下链接中: https://github.com/googleplus/gplus-quickstart-java/blob/master/src/com/google/plus/samples/quickstart/Signin.java

more information is in the flowing link: https://github.com/googleplus/gplus-quickstart-java/blob/master/src/com/google/plus/samples/quickstart/Signin.java

您还可以从以下链接获取Google API库:

Also you can get the Google API libraries from this link:

http://repo1.maven.org/maven2/com/google/

这篇关于无法在Cross Client谷歌oauth2.0中交换访问令牌和刷新令牌的授权代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆