I have an android app that authenticates using Firebase. I would like to access a Google endpoints api. I set arguments to the decorator #endpoints.api so that only this app can access it.
I was reading the tutorial at
https://cloud.google.com/endpoints/docs/frameworks/java/calling-from-android,
where it states to use the endpoints service object, I would first have to declare credentials as such,
credential = GoogleAccountCredential.usingAudience(this,
"server:client_id:1-web-app.apps.googleusercontent.com");
and then pass this credential to the service object builder.
However, since I authenticate with Firebase, this credential object would not work. An attempt to do so would output a GoogleAuthIOException error.
I cannot find any information on Firebase credential objects that would work for the service builder's third argument.
The closest I have come to is
AuthCredential credential = GoogleAuthProvider.getCredential(idToken, null);
I was reading up on some possible solutions such as at
Cloud Endpoints with Firebase Authentication, but this solution seems to suggest that I have to set up FirebaseAdminSDK on the endpoints service.
My major query is then this: for Firebase authentication on Android, do I have to pass its id token to the endpoint service which will then verify it using FirebaseAdminSDK as this second link suggests, or can I set up a simple Firebase credential variable to pass into the service object, similar to the example in this post's first link?
Related
I am developing using react-native with Amazon AWS as back-end. I am also using amplify library.
For logging in, I am using the below call:
const user = await Auth.signIn(...)
https://docs.amplify.aws/lib/auth/emailpassword/q/platform/js#sign-in
https://aws-amplify.github.io/amplify-js/api/classes/authclass.html#signin
My related questions:
I get a big JSON in the user variable. I believe it is the AWS credential?
The above signIn() call DOES NOT return a JWT token, it returns AWS credential. Is it? Also see the post:-
https://stackoverflow.com/a/59327655/15943787
Above post mentions:
After successfully authenticating a user, Amazon Cognito issues JSON
web tokens (JWT) that you can use to secure and authorize access to
your own APIs, or exchange for AWS credentials
What is meant by above statement? What is Auth.signIn() doing? getting JWT token or credentials?
Does this returned credential get stored inside the mobile device? By default in android it is stored un-encrypted. Right?
Is it stored in sharedPreferences?
The Auth.signIn() returns a CognitoUser Object as defined in the API Docs.
Below is from AWS SDK for Android but the data in Javascript is the same, (found here). The JWT Token in stored in the CognitoUser.CognitoUserSession attribute.
Represents a single Cognito User.
This class encapsulates all operations possible on a user and all tokens belonging to the user. The user tokens, as CognitoUserSession, are stored in SharedPreferences. Only the tokens belonging to the last successfully authenticated user are stored
So to answer your questions:
I get a big JSON in the user variable. I believe it is the AWS credential?
The user variable is a CognitoUser Object, which represents a single CognitoUser. to get the JWT token try user.sessionId or user.tokenId
The above signIn() call DOES NOT return a JWT token, it returns AWS credential. Is it? Also see the post:- https://stackoverflow.com/a/59327655/15943787
It updates the CognitoUser with session and token JWT. Check the user object to find them, after Auth.signIn() the user Object would have updated if AWS issued tokens
Does this returned credential get stored inside the mobile device? By default in android it is stored un-encrypted. Right?
Its being stores in Shared Preferences. so yes it's on device and un-encrypted. But AWS manages updating the and changing the JWT as the user interacts with cognito automatically.
Is it stored in sharedPreferences?
Yes, it is stored in sharedPreferences
As the title says, I'm trying to use the Google Sign-In API with a Spring Boot backend server, as described here.
Just to describe the context, the Spring backend is basically a resource+authentication server, that is currently providing Oauth2 authentication to a second spring boot application containing the frontend website, via Google SSO or simple form login (similar to what's described here).
My original idea was to mimic the #EnableOauth2Sso annotation by simply providing an access token to the android app and attach it to every request as "Bearer ".
Using the user credentials for this was pretty straightforward: I simply make a request to the server at "/oauth/token", using those credentials inserted by the user as authentication and I correctly receive the access token.
Now, I have absolutely no idea on how to build a similar procedure with the Google API in Android. The tutorial page I linked before describes how to get a token ID and how the server should validate it, but after that I don't know what to do.
So far I've managed to add a filter to the security chain that simply checks the token like this:
private Authentication attemptOpenIDAuthentication(#NonNull String tokenString){
String clientId = authServices.getClientId();
GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, factory)
.setAudience(Arrays.asList(clientId, androidClient))
.build();
try {
GoogleIdToken token = verifier.verify(tokenString);
if (token != null) {
return authServices.loadAuthentication(token.getPayload());
} else {
throw new InvalidTokenException("ID token is null");
}
} catch (GeneralSecurityException | IOException e) {
throw new BadCredentialsException("Could not validate ID token");
}
}
This manages indeed to create an Authentication object, but how can I generate an access token after the authentication filtering?
To recap, so far I've got:
The Android app successfully retrieves the Google token ID and sends it to the server
The server sucessfully intercepts the request and validates the token
I'm basically missing the third point where I return a proper access token to the Android client.
Here you are a simple scheme to better understand the situation:
Is there any other way to validate the token and get an access token from the server, or should I completely change the authentication procedure on Android?
As far as I can tell: Yes, you need an access token from the server. If I understand this correctly, a webapp is already authenticated via Oauth on your backend, so the procedure is similar here: Load the user with the google-ID and generate a token. In my application I used a JWT which is valid for 30 days. If the token expires, the Google authentication in the app is usually still valid, so the token can be renewed using the Google ID. With Oauth you can also send a refresh-token directly.
It is important that the app always checks the Google authentication first and only in a second step that of the backend.
For the Authentication process on the backend u may need to manually implement a dedicated securityConfiguration for this. Have a look at the jhipster project, they implemented a custom jwt-authentication which may give you an idea how it works.
I've got an API set up with Google Cloud Endpoints Frameworks. One of the endpoints requires auth which can apparently be done with Firebase Auth on Android. But the example given here is for Google accounts through Firebase. I'm just using email and password. So I'm using the HttpRequestInitializer here instead of the GoogleAccountCredential one. But when I attempt to make calls to the authenticated endpoint I get a 503 response with the server producing the following stack trace:
Uncaught exception from servlet
java.lang.IllegalStateException: method_info is not set in the request
at com.google.api.server.spi.auth.EspAuthenticator.authenticate(EspAuthenticator.java:67)
at com.google.api.server.spi.request.Auth.authenticate(Auth.java:85)
at com.google.api.server.spi.request.ServletRequestParamReader.getUser(ServletRequestParamReader.java:157)
at com.google.api.server.spi.request.ServletRequestParamReader.deserializeParams(ServletRequestParamReader.java:114)
at com.google.api.server.spi.request.ServletRequestParamReader.read(ServletRequestParamReader.java:261)
at com.google.api.server.spi.SystemService.invokeServiceMethod(SystemService.java:354)
at com.google.api.server.spi.SystemServiceServlet.execute(SystemServiceServlet.java:113)
at com.google.api.server.spi.SystemServiceServlet.doPost(SystemServiceServlet.java:71)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
...
Any ideas?
Firebase Auth itself is set up correctly because I can make calls to the Firebase real-time database to areas that need auth. And the endpoint is set up correctly because I can make unauthenticated calls to my other endpoints (which don't currently need authentication). It's just the combination of the two that is broken.
As mentioned in your Google Groups post, you need to remove the SystemServiceServlet from your config, and also remove the com.google.appengine:appengine-endpoints dependency from your list.
The online reference for Google APIs for Android, shows a public method summary for the Games class which includes:
static PendingResult<Games.GetTokenResult> getGamesAuthToken(GoogleApiClient apiClient)
But the latest release available (8.4.0) does not include this method. I use this to get the APIs:
dependencies {
compile 'com.google.android.gms:play-services:8.4.0'
}
Where is Games.getGamesAuthToken?
This is actually a documentation problem. getGamesAuthToken() has been removed because it was not as secure as it needs to be.
For reference, you can read http://android-developers.blogspot.com/2016/01/play-games-permissions-are-changing-in.html
The best way to handle this is to:
After the player is authenticated on the device:
Get an auth code to send to your backend server:
GetServerAuthCodeResult result =
Games.getGamesServerAuthCode(gac, clientId).await();
if (result.isSuccess()) {
String authCode = result.getCode();
// Send code to server.
}
On the server, exchange the auth code received for a token by
making an RPC to https://www.googleapis.com/oauth2/v4/token to exchange the auth code for an access token.
You’ll have to provide the server client ID, server client secret (listed in the Developer Console when you created the server client ID), and the auth code.
See more details here: https://developers.google.com/identity/protocols/OAuth2WebServer?utm_campaign=play games_discussion_permissions_012316&utm_source=anddev&utm_medium=blog#handlingresponse.
Once you have the access token, the player's ID is retrieved using:
www.googleapis.com/games/v1/applications/<app_id>/verify/ using the access token.
Pass the auth token in a header as follows:
“Authorization: OAuth ”
The response value will contain the player ID for the user. This is the correct player ID to use for this user.
This access token can be used to make additional server-to-server calls as needed.
I am quite new to google cloud endpoints and I would like know how to Use Auth with Endpoints, the tutorial here is good, but I don't understand this thing:
It says, that I should add a user(com.google.appengine.api.users.User
) parameter to backend's methods for auth. If I want to use android as client part, I should provide GoogleAccountCredential object to make an authenticated call [2]. The GoogleAccountCredential is created this way
credential = GoogleAccountCredential.usingAudience(this,
"server:client_id:1-web-app.apps.googleusercontent.com");
credential.setSelectedAccountName(accountName);
The accountName is the name of a Google Account, so I assume, that everyone, who has the Google Account and is using my Google Cloud Endpoint application can create the GoogleAccountCredential object and make an authenticated call to backend.
But there are obviously many methods in my backend, which can be invoked only by some users of my app. (example: There is a method, which will give me a details about my friend, it's clear that this method can be called only by his friends.). Hence my question is: Is there any way to map the com.google.appengine.api.users.User to some my custom User entity, to be possible to check whether the User is really authorized to call the backend's method and not only to know that the method was called by a User with Google Account ? Should I write my custom Authenticator for this, if so, could you advise me how ?
Thank you!
You can set an Authenticator class which will handle the custom authentication.
https://cloud.google.com/appengine/docs/java/endpoints/javadoc/com/google/api/server/spi/config/Authenticator
you just need to set the authenticators param in the #ApiMethod and you can write your own authentication logic