Log in to the application from the android client

I'm trying to log in to connect to the application engine and access the user API in the application. Basically I want to see who goes into my servlets. I use the authentication flow, getting authtoken from android, and then getting the ASID (or SACID) cookie from the application engine. Then the cookie is sent with an HTTP request to the servlet application. Everything seems to work fine, however, when I try to get the user with this code:

UserService userService = UserServiceFactory.getUserService(); User user= userService.getCurrentUser(); 
User

always null. My question is: did I miss something? Why does the user service return a null user? Below is the application and android code. Any help would be greatly appreciated!

Application Engine:

 public class MyServlet extends HttpServlet { public void process(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { resp.setContentType("text/plain"); UserService userService = UserServiceFactory.getUserService(); User user= userService.getCurrentUser(); } public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { process(req, resp); } public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { process(req, resp); } } 

Android Code:

 public class AppEngineClient { static final String BASE_URL = Util.getBaseUrl(this); private static final String AUTH_URL = BASE_URL + "/_ah/login"; private static final String AUTH_TOKEN_TYPE = "ah"; private final Context mContext; private final String mAccountName; private static final String TAG = "AppEngineClient"; public AppEngineClient(Context context, String accountName) { this.mContext = context; this.mAccountName = accountName; } public HttpResponse makeRequest(String urlPath, List<NameValuePair> params) throws Exception { HttpResponse res = makeRequestNoRetry(urlPath, params, false); if (res.getStatusLine().getStatusCode() == 500) { res = makeRequestNoRetry(urlPath, params, true); } return res; } private HttpResponse makeRequestNoRetry(String urlPath, List<NameValuePair> params, boolean newToken) throws Exception { // Get auth token for account Account account = new Account(mAccountName, "com.google"); String authToken = getAuthToken(mContext, account); if (newToken) { // invalidate the cached token AccountManager accountManager = AccountManager.get(mContext); accountManager.invalidateAuthToken(account.type, authToken); authToken = getAuthToken(mContext, account); } // Get SACSID cookie DefaultHttpClient client = new DefaultHttpClient(); String continueURL = BASE_URL; URI uri = new URI(AUTH_URL + "?continue=" + URLEncoder.encode(continueURL, "UTF-8") + "&auth=" + authToken); HttpGet method = new HttpGet(uri); final HttpParams getParams = new BasicHttpParams(); HttpClientParams.setRedirecting(getParams, false); // continue is not used method.setParams(getParams); HttpResponse res = client.execute(method); Header[] headers = res.getHeaders("Set-Cookie"); if (res.getStatusLine().getStatusCode() != 302 || headers.length == 0) { return res; } String sascidCookie = null; for (Header header: headers) { if (header.getValue().indexOf("SACSID=") >=0) { // let parse it String value = header.getValue(); String[] pairs = value.split(";"); ascidCookie = pairs[0]; } } // Make POST request uri = new URI(BASE_URL + urlPath); HttpPost post = new HttpPost(uri); UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, "UTF-8"); post.setEntity(entity); post.setHeader("Cookie", ascidCookie); post.setHeader("X-Same-Domain", "1"); // XSRF res = client.execute(post); return res; } private String getAuthToken(Context context, Account account) throws PendingAuthException { String authToken = null; AccountManager accountManager = AccountManager.get(context); try { AccountManagerFuture<Bundle> future = accountManager.getAuthToken (account, AUTH_TOKEN_TYPE, false, null, null); Bundle bundle = future.getResult(); authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN); if (authToken == null) { throw new PendingAuthException(bundle); } } catch (OperationCanceledException e) { Log.w(TAG, e.getMessage()); } catch (AuthenticatorException e) { Log.w(TAG, e.getMessage()); } catch (IOException e) { Log.w(TAG, e.getMessage()); } return authToken; } public class PendingAuthException extends Exception { private static final long serialVersionUID = 1L; private final Bundle mAccountManagerBundle; public PendingAuthException(Bundle accountManagerBundle) { super(); mAccountManagerBundle = accountManagerBundle; } public Bundle getAccountManagerBundle() { return mAccountManagerBundle; } } 

}

+4
source share
2 answers

The Android code above gets the ClientLogin token from the Google Accounts API. To log in and get the current user through UserService , the GAE application must use the Google Accounts API for authentication ("Application Settings" β†’ "Authentication Settings").

+3
source

I noticed that adding authentication to an existing AppEngine endpoint is very simple - you just need to add the com.google.appengine.api.users.User parameter to your method. And finally, I discovered what is happening under the hood, and how to authenticate an arbitrary servlet in the same way. Therefore, for authentication on the Android side you will need: 1) select an account:

 private void signIn() { startActivityForResult(GoogleAccountCredential.usingAudience(this, "server:client_id:{id}.apps.googleusercontent.com").newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case REQUEST_ACCOUNT_PICKER: if (data != null && data.getExtras() != null) { String accountName = data.getExtras().getString(AccountManager.KEY_ACCOUNT_NAME); if (accountName != null) { // TODO save accountName } } break; } } 

2) get a Credential object:

 GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(this, "server:client_id:{id}.apps.googleusercontent.com"); credential.setSelectedAccountName(accountName); 

3) create a Google HttpRequest object and execute the request:

 HttpTransport transport = new NetHttpTransport(); HttpRequestFactory requestFactory = transport.createRequestFactory(credential); GenericUrl url = new GenericUrl(UPLOAD_SERVICE_URL); HttpRequest request = requestFactory.buildGetRequest(url); HttpResponse resp = request.execute(); // TODO check response 

To authenticate a request on the AppEngine side, you can use the WebApisUserService inner class declared in the appengine-endpoints.jar library. This is just the class used by AppEngine inside endpoints. Unfortunately, this class constructor and other necessary methods are protected from external use, so we need to use reflection to access it. The full helper class is as follows:

 public class WebApisUserServiceHelper { public static WebApisUserService createInstance(boolean isClientIdWhitelistEnabled) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { Constructor<WebApisUserService> constructor; constructor = WebApisUserService.class.getDeclaredConstructor(Boolean.TYPE); constructor.setAccessible(true); WebApisUserService ret = constructor.newInstance(isClientIdWhitelistEnabled); return ret; } public static User getCurrentUser(WebApisUserService service, HttpServletRequest req, String appName, List<String> audiences, List<String> clientIds) throws NoSuchMethodException, SecurityException, ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { String token = getAuthToken(service, req); if (token != null) { List<String> allowedScopes = new ArrayList<String>(); allowedScopes.add("https://www.googleapis.com/auth/userinfo.email"); return getCurrentUser(service, token, allowedScopes, audiences, clientIds); } return null; } private static User getCurrentUser(WebApisUserService service, String token, List<String> allowedScopes, List<String> allowedAudiences, List<String> allowedClientIds) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Method method = WebApisUserService.class.getDeclaredMethod("getCurrentUser", String.class, List.class, List.class, List.class); method.setAccessible(true); Object ret = method.invoke(service, token, allowedScopes, allowedAudiences, allowedClientIds); if (ret instanceof User) return (User) ret; return null; } private static String getAuthToken(WebApisUserService service, HttpServletRequest request) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Method method = WebApisUserService.class.getDeclaredMethod("getAuthToken", HttpServletRequest.class); method.setAccessible(true); Object ret = method.invoke(service, request); if (ret instanceof String) return (String) ret; return null; } } 

and here is how to use this helper:

 public class MyServlet extends HttpServlet { private final WebApisUserService auth = createAuthService(); private static WebApisUserService createAuthService() { try { return WebApisUserServiceHelper.createInstance(false); } catch (Exception e) { log.log(Level.WARNING, "Failed to create WebApisUserServiceFactory instance. Exception: %s", e.toString()); } return null; } @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { try { User user = authenticateUserSafe(req); if (user == null) { resp.sendError(401, "auth required"); return; } String str = String.format("User id: %s, nick: %s, email: %s", user.getUserId(), user.getNickname(), user.getEmail()); resp.getWriter().write(str); } catch (Throwable e) { resp.getWriter().write("Exception: " + e); } } private User authenticateUserSafe(HttpServletRequest req) { try { return authenticateUser(req); } catch (Exception e) { log.log(Level.WARNING, "Failed to authenticate user. Exception: %s", e.toString()); } return null; } private User authenticateUser(HttpServletRequest req) throws NoSuchMethodException, SecurityException, ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { List<String> audiences = new ArrayList<String>(); audiences.add(Ids.ANDROID_AUDIENCE); List<String> clientIds = new ArrayList<String>(); clientIds.add(Ids.WEB_CLIENT_ID); clientIds.add(Ids.ANDROID_CLIENT_ID); return WebApisUserServiceHelper.getCurrentUser(auth, req, "{id}", audiences, clientIds); } } 

This approach has been tested with AppEngine 1.8.6. I hope that Google will open the WebApisUserService class publicly, so no response is needed.

0
source

Source: https://habr.com/ru/post/1402738/


All Articles