Allright, I have been struggling in understanding this authentification process with this "template" project and just don't get it.
What I am trying to do is a basic sign in, so I can get my authorization token, I've done it before by using SharedPreference (never actually used AccountManager) and accessing it from my rest android client (via a custom SessionManager).
So far, I was able to get the first part done. I am able to get my authorization token.
BoostrapAuthenticatorActivity.java:
/**
* Called when response is received from the server for authentication
* request. See onAuthenticationResult(). Sets the
* AccountAuthenticatorResult which is sent back to the caller. Also sets
* the authToken in AccountManager for this account.
*/
protected void finishLogin() {
final Account account = new Account(email, Constants.Auth.LZGO_ACCOUNT_TYPE);
if (requestNewAccount)
accountManager.addAccountExplicitly(account, password, null);
else
accountManager.setPassword(account, password);
final Intent intent = new Intent();
authToken = token;
userToken = user;
intent.putExtra(KEY_ACCOUNT_NAME, userToken);
intent.putExtra(KEY_ACCOUNT_TYPE, Constants.Auth.LZGO_ACCOUNT_TYPE);
if (authTokenType != null
&& authTokenType.equals(Constants.Auth.AUTHTOKEN_TYPE))
intent.putExtra(KEY_AUTHTOKEN, authToken);
setAccountAuthenticatorResult(intent.getExtras());
setResult(RESULT_OK, intent);
finish();
}
How do I have access to this value ? If you check this class,
BootstrapServiceProvider.java:
#Inject private ApiKeyProvider keyProvider;
#Inject private UserAgentProvider userAgentProvider;
/**
* Get service for configured key provider
* <p>
* This method gets an auth key and so it blocks and shouldn't be called on the main thread.
*
* #return bootstrap service
* #throws IOException
* #throws AccountsException
*/
public BootstrapService getService() throws IOException, AccountsException {
return new BootstrapService(keyProvider.getAuthKey(), userAgentProvider);
}
And finally, the provider:
ApiKeyProvider.java:
#Inject private Activity activity;
#Inject private AccountManager accountManager;
/**
* This call blocks, so shouldn't be called on the UI thread
*
* #return API key to be used for authorization with a {#link com.android.lzgo.core.LzgoService} instance
* #throws AccountsException
* #throws IOException
*/
public String getAuthKey() throws AccountsException, IOException {
AccountManagerFuture<Bundle> accountManagerFuture = accountManager.getAuthTokenByFeatures(Constants.Auth.BOOTSTRAP_ACCOUNT_TYPE,
Constants.Auth.AUTHTOKEN_TYPE, new String[0], activity, null, null, null, null);
Log.d("ApiKeyProvider", "ApiKeyProvider= " + accountManagerFuture.getResult().getString(KEY_AUTHTOKEN));
return accountManagerFuture.getResult().getString(KEY_AUTHTOKEN);
}
But this get me a null value! I'm at lost!
I'm not sure why the authtoken isn't getting applied correctly by setAccountAuthenticatorResult(...), however I've had luck by adding the following accountManager.setAuthToken(...) call in finishLogin()
if (authTokenType != null
&& authTokenType.equals(Constants.Auth.AUTHTOKEN_TYPE)) {
intent.putExtra(KEY_AUTHTOKEN, authToken);
accountManager.setAuthToken(account, authTokenType, authToken);
}
Perhaps this is related to not using Parse.com as our backend?
Related
I have an application where the smartphone must connect via SSLSocket to a local server. I tested my app on 5 different smartphones: Moto G2 (6.0), Redmi 3S (6.0.1), LG K5 (6.0), Moto G5 Plus (7.1.1) and OnePlus 5 (8.0). The Moto G5 Plus was the only one to show this problem.
This is the line that causes the problematic behaviour. All tests where done on the same network.
socket = (SSLSocket) sslContext.getSocketFactory().createSocket(serverAddress, serverPort);
Is there any known problem with Moto G5 Plus or with Android 7+ around this behaviour?
EDIT: Some more tests are leading to an idea of Android system trying to force the Socket to connect through mobile network when identifying that WiFi interface is connected, but with no internet. Is there any way to enforce the Socket to use WiFi instead of mobile network?
Disclaimer: I didn't test this so I'm really not sure if it works.
The Network class has a bind(Socket) method, maybe you could find the wifi network and then bind it to your socket. From the doc it seems that this is what you need, it says:
/**
* Binds the specified {#link Socket} to this {#code Network}. All data traffic on the socket
* will be sent on this {#code Network}, irrespective of any process-wide network binding set by
* {#link ConnectivityManager#bindProcessToNetwork}. The socket must not be connected.
*/
The Socket shouldn't be connected before binding to the network, so I think you should create it with socketFactory.createSocket() and connect it only after the binding.
So, you should first find your Network (Kotlin):
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val wifiNetwork = connectivityManager.allNetworks.firstOrNull {
val info = connectivityManager.getNetworkInfo(it)
info.type == ConnectivityManager.TYPE_WIFI
}
or (Java)
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
Network wifiNetwork = null;
for(Network network : connectivityManager.getAllNetworks()){
NetworkInfo networkInfo = connectivityManager.getNetworkInfo(network);
if(networkInfo.getType() == ConnectivityManager.TYPE_WIFI){
wifiNetwork = network;
break;
}
}
Then bind it to the Socket and finally connect (Kotlin):
wifiNetwork?.bindSocket(socket)
val socketAddress = InetSocketAddress(hostname, port)
socket.connect(socketAddress)
or (Java)
if(wifiNetwork != null){
wifiNetwork.bindSocket(socket);
}
InetSocketAddress socketAddress = InetSocketAddress(hostName, port);
socket.connect(socketAddress);
Note, it needs ACCESS_NETWORK_STATE permission
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
i hope it helps you,i just found your solution on github.
for further details and for official link,please check this out, i think this can be helpful to you.
if it is not the answer,please ignore this answer.
We are using AsyncTask to avoid StrictMode fatal error for network access ( Look in references ). The StrictMode policy is simply forbidding us to affect on UI Thread.
/* AsyncTask class which manages connection with server app and is sending shutdown command.
*/
public class ShutdownAsyncTask extends AsyncTask<String, String, TCPClient> {
private static final String COMMAND = "shutdown -s" ;
private TCPClient tcpClient ;
private Handler mHandler ;
private static final String TAG = "ShutdownAsyncTask";
/**
* ShutdownAsyncTask constructor with handler passed as argument. The UI is updated via handler.
* In doInBackground(...) method, the handler is passed to TCPClient object.
* #param mHandler Handler object that is retrieved from MainActivity class and passed to TCPClient
* class for sending messages and updating UI.
*/
public ShutdownAsyncTask(Handler mHandler){
this.mHandler = mHandler;
}
/**
* Overriden method from AsyncTask class. There the TCPClient object is created.
* #param params From MainActivity class empty string is passed.
* #return TCPClient object for closing it in onPostExecute method.
*/
#Override
protected TCPClient doInBackground(String... params) {
Log.d(TAG, "In do in background");
try{
tcpClient = new TCPClient(mHandler,
COMMAND,
"192.168.1.1",
new TCPClient.MessageCallback() {
#Override
public void callbackMessageReceiver(String message) {
publishProgress(message);
}
});
}catch (NullPointerException e){
Log.d(TAG, "Caught null pointer exception");
e.printStackTrace();
}
tcpClient.run();
return null;
}
In this AsyncTask we are creating TCPClient object ( explained below ). In the TCPClient constructor we are passing Handler object for changing the UI, COMMAND - the String with the "shutdown -s" command for shutting down the computer, IP number - the servers ip number; Callback object - when we are getting servers response, the callback method 'messageCallbackReceiver' is starting 'publishProgress' method, which is publishing progress to 'onProgressUpdate' AsyncTask's method.
/**
* Overriden method from AsyncTask class. Here we're checking if server answered properly.
* #param values If "restart" message came, the client is stopped and computer should be restarted.
* Otherwise "wrong" message is sent and 'Error' message is shown in UI.
*/
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
Log.d(TAG, "In progress update, values: " + values.toString());
if(values[0].equals("shutdown")){
tcpClient.sendMessage(COMMAND);
tcpClient.stopClient();
mHandler.sendEmptyMessageDelayed(MainActivity.SHUTDOWN, 2000);
}else{
tcpClient.sendMessage("wrong");
mHandler.sendEmptyMessageDelayed(MainActivity.ERROR, 2000);
tcpClient.stopClient();
}
}
After receiving proper message, we are sending command, or if we received wrong message, we are sending message "wrong" and stopping client. After this we are being transferred to the 'onPostExecute' method:
#Override
protected void onPostExecute(TCPClient result){
super.onPostExecute(result);
Log.d(TAG, "In on post execute");
if(result != null && result.isRunning()){
result.stopClient();
}
mHandler.sendEmptyMessageDelayed(MainActivity.SENT, 4000);
}
}
So step by step:
->AsyncTask is creating TCPClient object.
->In TCPClient constructor we are passing Handler, Command, IP Number and Callback object.
->When TCPClient begins connection, it sends message "shutdown" to the server.
->When we are receiving message from server, the callback is passing it to 'onProgressUpdate'.
->If the received message ( response from server ) is equal to "shutdown", we are sending COMMAND to server.
->After sending it we are stopping client, which is transferring us to 'onPostExecute' method.
->Meanwhile, the handler is receiving empty messages with 'msg.what' integers defined in MainActivity, which are responsible for updating the GUI.
Example of how the widget UI is updated:
mHandler = new Handler(){
public void handleMessage(Message msg) {
switch(msg.what){
case SHUTDOWN:
Log.d(mTag, "In Handler's shutdown");
views = new RemoteViews(context.getPackageName(), R.layout.activity_main);
widget = new ComponentName(context, MainActivity.class);
awManager = AppWidgetManager.getInstance(context);
views.setTextViewText(R.id.state, "Shutting PC...");
awManager.updateAppWidget(widget,views);
break;
TCPClient
This class is responsible for maintaining the connection. I will explain it step by step:
In the first step we can see the objects passed from ShutdownAsyncTask and others. Additionally we can see the sendMessage and stopClient methods.
public class TCPClient {
private static final String TAG = "TCPClient" ;
private final Handler mHandler ;
private String ipNumber, incomingMessage, command;
BufferedReader in ;
PrintWriter out ;
private MessageCallback listener = null ;
private boolean mRun = false ;
/**
* TCPClient class constructor, which is created in AsyncTasks after the button click.
* #param mHandler Handler passed as an argument for updating the UI with sent messages
* #param command Command passed as an argument, e.g. "shutdown -r" for restarting computer
* #param ipNumber String retrieved from IpGetter class that is looking for ip number.
* #param listener Callback interface object
*/
public TCPClient(Handler mHandler, String command, String ipNumber, MessageCallback listener) {
this.listener = listener;
this.ipNumber = ipNumber;
this.command = command ;
this.mHandler = mHandler;
}
/**
* Public method for sending the message via OutputStream object.
* #param message Message passed as an argument and sent via OutputStream object.
*/
public void sendMessage(String message){
if (out != null && !out.checkError()) {
out.println(message);
out.flush();
mHandler.sendEmptyMessageDelayed(MainActivity.SENDING, 1000);
Log.d(TAG, "Sent Message: " + message);
}
}
/**
* Public method for stopping the TCPClient object ( and finalizing it after that ) from AsyncTask
*/
public void stopClient(){
Log.d(TAG, "Client stopped!");
mRun = false;
}
The magic happens here - in 'run()' method. Here we are using 'try-catch' tools for handling exceptions ( server not enabled, ip not proper etc. ). As you can see below, we have infinite while() loop for listening to the incoming messages. We can simply stop it and finalize with 'stopClient()' method ( used in ShutdownAsyncTask's 'onProgressUpdate' method)
public void run() {
mRun = true;
try {
// Creating InetAddress object from ipNumber passed via constructor from IpGetter class.
InetAddress serverAddress = InetAddress.getByName(ipNumber);
Log.d(TAG, "Connecting...");
/**
* Sending empty message with static int value from MainActivity
* to update UI ( 'Connecting...' ).
*
* #see com.example.turnmeoff.MainActivity.CONNECTING
*/
mHandler.sendEmptyMessageDelayed(MainActivity.CONNECTING,1000);
/**
* Here the socket is created with hardcoded port.
* Also the port is given in IpGetter class.
*
* #see com.example.turnmeoff.IpGetter
*/
Socket socket = new Socket(serverAddress, 4444);
try {
// Create PrintWriter object for sending messages to server.
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
//Create BufferedReader object for receiving messages from server.
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
Log.d(TAG, "In/Out created");
//Sending message with command specified by AsyncTask
this.sendMessage(command);
//
mHandler.sendEmptyMessageDelayed(MainActivity.SENDING,2000);
//Listen for the incoming messages while mRun = true
while (mRun) {
incomingMessage = in.readLine();
if (incomingMessage != null && listener != null) {
/**
* Incoming message is passed to MessageCallback object.
* Next it is retrieved by AsyncTask and passed to onPublishProgress method.
*
*/
listener.callbackMessageReceiver(incomingMessage);
}
incomingMessage = null;
}
Log.d(TAG, "Received Message: " +incomingMessage);
} catch (Exception e) {
Log.d(TAG, "Error", e);
mHandler.sendEmptyMessageDelayed(MainActivity.ERROR, 2000);
} finally {
out.flush();
out.close();
in.close();
socket.close();
mHandler.sendEmptyMessageDelayed(MainActivity.SENT, 3000);
Log.d(TAG, "Socket Closed");
}
} catch (Exception e) {
Log.d(TAG, "Error", e);
mHandler.sendEmptyMessageDelayed(MainActivity.ERROR, 2000);
}
}
The last thing in the client is the Callback interface. We have it in the TCPClient class in the end:
/**
* Callback Interface for sending received messages to 'onPublishProgress' method in AsyncTask.
*
*/
public interface MessageCallback {
/**
* Method overriden in AsyncTask 'doInBackground' method while creating the TCPClient object.
* #param message Received message from server app.
*/
public void callbackMessageReceiver(String message);
}
I am using smack and openfire for create chat app in android .
for message status I have no problem with delivered and displayed message in other
client (double check).
I will send a simple json message like bellow to sender:
{"delivery":timestapmp}
and parse it and double check messages with lower than timestamp that sent before.
the problem is about sent status (one check).
When i send message the server no response anything that message has sent .
is it possible in smack to send message with callback from server.
if possible and is it possible to send time server in callback response .
thanks .
private void acknowledgementFromServer(final Message message) throws StreamManagementException.StreamManagementNotEnabledException {
if (connection != null && connection.isSmEnabled()) {
connection.addStanzaIdAcknowledgedListener(message.getStanzaId(), new StanzaListener() {
#Override
public void processStanza(Stanza packet) throws SmackException.NotConnectedException, InterruptedException {
MessageAsyncTask task = new MessageAsyncTask(packet.getStanzaId(), MSG_STATUS_SENT);
task.execute();
}
});
}
Hey you can do it like this.. call method every time you send message by passing that message as a parameter in above method
Note: Stream Management should be enabled for this to work, can be done like below:
DeliveryReceiptManager.setDefaultAutoReceiptMode(DeliveryReceiptManager.AutoReceiptMode.always);
ProviderManager.addExtensionProvider(DeliveryReceipt.ELEMENT, DeliveryReceipt.NAMESPACE, new DeliveryReceipt.Provider());
ProviderManager.addExtensionProvider(DeliveryReceiptRequest.ELEMENT, DeliveryReceipt.NAMESPACE, new DeliveryReceiptRequest.Provider());
According to my knowledge I have got up to this Inteface : ReceiptReceivedListener which is in smack 4.2
below is how I have implemented this :
private ReceiptReceivedListener receiptReceivedListener;
/**
* get DeliveryReceiptManager
*
* #return
*/
private DeliveryReceiptManager getDeliveryReceiptManager() {
if (deliveryReceiptManager == null && getConnection() != null) {
deliveryReceiptManager = DeliveryReceiptManager.getInstanceFor(getConnection());
}
return deliveryReceiptManager;
}
add Listener
getDeliveryReceiptManager().addReceiptReceivedListener(receiptReceivedListener);
Received the call back
receiptReceivedListener = new ReceiptReceivedListener() {
#Override
public void onReceiptReceived(Jid fromJid, Jid toJid, String receiptId, Stanza receipt) {
//TODO : on recieved status of message delivery
}
};
This will help you for sure
Below is the Interface for Smack 4.2 with full details :
/**
* Callback invoked when a new receipt got received.
* <p>
* {#code receiptId} correspondents to the message ID, which can be obtained with
* {#link org.jivesoftware.smack.packet.Stanza#getStanzaId()}.
* </p>
*
* #param fromJid the jid that send this receipt
* #param toJid the jid which received this receipt
* #param receiptId the message ID of the stanza(/packet) which has been received and this receipt is for
* #param receipt the receipt
*/
void onReceiptReceived(Jid fromJid, Jid toJid, String receiptId, Stanza receipt);
I just complete a android app based on a website tutorial. This app is to send and receive data to google datastore. I have created a appengine backend. This works well locally on localhost:8888. I could see the data transformation. But after I deploy it to google app engine. It can not show the data. I could access the datastore by myapp.appspot.com/_ah/api/explorer. But I can not access it with phone while I can access local data with phone emulator. I just followed this gentleman's guide https://github.com/sachinkariyattin/Cloudendpoints
Any one can help me? Thanks in advance.
The Below is the CloudEndpointUtils class
package com.iot1;
import com.google.api.client.googleapis.json.GoogleJsonError;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.googleapis.services.AbstractGoogleClient;
import com.google.api.client.googleapis.services.AbstractGoogleClientRequest;
import com.google.api.client.googleapis.services.GoogleClientRequestInitializer;
import android.app.Activity;
import android.util.Log;
import android.widget.Toast;
import java.io.IOException;
/**
* Common utilities for working with Cloud Endpoints.
*
* If you'd like to test using a locally-running version of your App Engine
* backend (i.e. running on the Development App Server), you need to set
* LOCAL_ANDROID_RUN to 'true'.
*
* See the documentation at
* http://developers.google.com/eclipse/docs/cloud_endpoints for more
* information.
*/
public class CloudEndpointUtils {
/*
* TODO: Need to change this to 'true' if you're running your backend locally using
* the DevAppServer. See
* http://developers.google.com/eclipse/docs/cloud_endpoints for more
* information.
*/
protected static final boolean LOCAL_ANDROID_RUN = true;
/*
* The root URL of where your DevAppServer is running (if you're running the
* DevAppServer locally).
*/
protected static final String LOCAL_APP_ENGINE_SERVER_URL = "http://localhost:8888/";
/*
* The root URL of where your DevAppServer is running when it's being
* accessed via the Android emulator (if you're running the DevAppServer
* locally). In this case, you're running behind Android's virtual router.
* See
* http://developer.android.com/tools/devices/emulator.html#networkaddresses
* for more information.
*/
protected static final String LOCAL_APP_ENGINE_SERVER_URL_FOR_ANDROID = "http://10.0.2.2:8888";
/**
* Updates the Google client builder to connect the appropriate server based
* on whether LOCAL_ANDROID_RUN is true or false.
*
* #param builder
* Google client builder
* #return same Google client builder
*/
public static <B extends AbstractGoogleClient.Builder> B updateBuilder(
B builder) {
if (LOCAL_ANDROID_RUN) {
builder.setRootUrl(LOCAL_APP_ENGINE_SERVER_URL_FOR_ANDROID
+ "/_ah/api/");
}
// only enable GZip when connecting to remote server
final boolean enableGZip = builder.getRootUrl().startsWith("https:");
builder.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
#Override
public void initialize(AbstractGoogleClientRequest<?> request)
throws IOException {
if (!enableGZip) {
request.setDisableGZipContent(true);
}
}
});
return builder;
}
/**
* Logs the given message and shows an error alert dialog with it.
*
* #param activity
* activity
* #param tag
* log tag to use
* #param message
* message to log and show or {#code null} for none
*/
public static void logAndShow(Activity activity, String tag, String message) {
Log.e(tag, message);
showError(activity, message);
}
/**
* Logs the given throwable and shows an error alert dialog with its
* message.
*
* #param activity
* activity
* #param tag
* log tag to use
* #param t
* throwable to log and show
*/
public static void logAndShow(Activity activity, String tag, Throwable t) {
Log.e(tag, "Error", t);
String message = t.getMessage();
// Exceptions that occur in your Cloud Endpoint implementation classes
// are wrapped as GoogleJsonResponseExceptions
if (t instanceof GoogleJsonResponseException) {
GoogleJsonError details = ((GoogleJsonResponseException) t)
.getDetails();
if (details != null) {
message = details.getMessage();
}
}
showError(activity, message);
}
/**
* Shows an error alert dialog with the given message.
*
* #param activity
* activity
* #param message
* message to show or {#code null} for none
*/
public static void showError(final Activity activity, String message) {
final String errorMessage = message == null ? "Error" : "[Error ] "
+ message;
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(activity, errorMessage, Toast.LENGTH_LONG)
.show();
}
});
}
}
I guess you did not create your own account at Google Cloud and/or make the necessary changes in appengine-web.xml file. For a thorough example you may follow: http://rominirani.com/2014/08/20/gradle-tutorial-part-7-android-studio-app-engine-gradle/
I found it that I should update the created google client library after I changed the appengine-web.xml. And it all works now.
I am trying to use a Developer Authenticated Provider to login to my android app basing it loosely off of this demo: https://github.com/awslabs/aws-sdk-android-samples/tree/master/CognitoSyncDemo. I successfully logged in through our own backend got the idToken and subsequently got session credentials to access our AWS database. I then used those credentials to make a POST to the db.
But this only worked once, now I cannot get through again, without having changed any code. I am also using a generated SDK through http://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-generate-sdk.html. I'm not sure if this is causing any errors.
Here is my DeveloperAuthenticationProvider:
public class AuthenticationProvider extends
AWSAbstractCognitoDeveloperIdentityProvider {
private static final String TAG = AuthenticationProvider.class.getSimpleName();
private static SocializeClient mSocializeClient;
private static final String developerProvider = Constants.AWS_PROVIDER_NAME;
private static final String cognitoSampleDeveloperAuthenticationAppEndpoint = UrlEndpoints.URL_DOMAIN;
public AuthenticationProvider(Context context, String accountId, String identityPoolId, Regions region) {
super(accountId, identityPoolId, region);
/*
* Initialize the client using which you will communicate with your
* backend for user authentication.
*/
AWSCredentialsProvider awsCredentialsProvider = new CognitoCachingCredentialsProvider(
context,
this,
region
);
ApiClientFactory factory = new ApiClientFactory()
.endpoint(cognitoSampleDeveloperAuthenticationAppEndpoint)
.credentialsProvider(awsCredentialsProvider);
mSocializeClient = factory.build(SocializeClient.class);
}
/*Only refreshes the login info, when it has expired*/
/*
* (non-Javadoc)
* #see com.amazonaws.auth.AWSCognitoIdentityProvider#refresh() In refresh
* method, you will have two flows:
*/
/*
* 1. When the app user uses developer authentication. In this case, make
* the call to your developer backend, from where call the
* GetOpenIdTokenForDeveloperIdentity API of Amazon Cognito service. Be sure to call update(), so as to
* set the identity id and the token received.
*/
/*
* 2.When the app user is not using the developer authentication, just call
* the refresh method of the AWSAbstractCognitoDeveloperIdentityProvider
* class which actually calls GetId and GetOpenIDToken API of Amazon
* Cognito.
*/
#Override
public String refresh() {
Log.i(TAG, "refresh");
// If there is a key with developer provider name in the logins map, it
// means the app user has used developer credentials
if (!loginsMap.isEmpty()
&& loginsMap.containsKey(developerProvider)) {
Log.i(TAG, "contains provider");
} else {
Log.i(TAG, "does not contain developer provider");
Map<String, String> logins = new HashMap<>();
logins.put(developerProvider, UserSingleton.imei);
setLogins(logins);
}
// TODO:: Temp code to login. Once available, need to add code to GetToken from SocializeClient
Login login = new Login();
login.setImei(UserSingleton.imei);
login.setPassword(UserSingleton.password);
LoginReponse loginReponse = mSocializeClient.socializeAuthLoginPost(login);
Log.i(TAG, "login response: " + loginReponse.getIdentityId() + " - token: " + loginReponse.getToken());
update(loginReponse.getIdentityId(), loginReponse.getToken());
Log.i(TAG, "updated");
return loginReponse.getToken();
}
/*
* (non-Javadoc)
* #see com.amazonaws.auth.AWSBasicCognitoIdentityProvider#getIdentityId()
*/
/*
* This method again has two flows as mentioned above depending on whether
* the app user is using developer authentication or not. When using
* developer authentication system, the identityId should be retrieved from
* the developer backend. In the other case the identityId will be retrieved
* using the getIdentityId() method which in turn calls Cognito GetId and
* GetOpenIdToken APIs.
*/
#Override
public String getIdentityId() {
Log.i(TAG, "getIdentityId");
identityId = CognitoSyncClientManager.credentialsProvider.getCachedIdentityId();
if (identityId == null) {
Log.i(TAG, "identityId is null");
if (!loginsMap.isEmpty()
&& loginsMap.containsKey(developerProvider)) {
Log.i(TAG, "grabbing identityId using logins map");
// TODO:: Temp code to login. Once available, need to add code to GetToken from SocializeClient
Login login = new Login();
login.setImei(loginsMap.get(developerProvider));
login.setPassword(UserSingleton.password);
LoginReponse loginReponse = mSocializeClient.socializeAuthLoginPost(login);
Log.i(TAG, "login response: " + loginReponse.getIdentityId() + " - token: " + loginReponse.getToken());
update(loginReponse.getIdentityId(), loginReponse.getToken());
return loginReponse.getIdentityId();
} else {
return super.getIdentityId();
}
} else {
return identityId;
}
}
/*
* (non-Javadoc)
* #see
* com.amazonaws.auth.AWSAbstractCognitoIdentityProvider#getProviderName()
* Return the developer provider name which you chose while setting up the
* identity pool in the Amazon Cognito Console
*/
#Override
public String getProviderName() {
return developerProvider;
}
/**
* This function validates the user credentials against the sample Cognito
* developer authentication application. After that it stores the key and
* token received from sample Cognito developer authentication application
* for all further communication with the application.
*
* #param imei
* #param password
*/
public void login(String imei, String password, Context context) {
Log.i(TAG, "login");
Login login = new Login();
login.setImei(imei);
login.setPassword(password);
new AuthenticationTask(context).execute(login);
}
public void publishProfile(Context context, Profile profile){
Log.i(TAG, "publishProfile");
ProfileKey profileKey = new ProfileKey();
profileKey.setUserID(identityId);
profile.setKey(profileKey);
new UploadProfileTask(context).execute(profile);
}
protected static SocializeClient getSocializeClientInstance() {
if (mSocializeClient == null) {
throw new IllegalStateException(
"Dev Auth Client not initialized yet");
}
return mSocializeClient;
}
}
Here is my AuthenticationTask as well where I attempt to login, then grab credentials to access the AWS database:
public class AuthenticationTask extends
AsyncTask<Login, Void, Void> {
private static final String TAG = AuthenticationTask.class.getSimpleName();
// The user name or the developer user identifier you will pass to the
// Amazon Cognito in the GetOpenIdTokenForDeveloperIdentity API
private String mImei;
private String mPassword;
private GetCredentialsForIdentityResult credentialsForIdentityResult;
private boolean isSuccessful;
private final Context context;
public AuthenticationTask(Context context) {
this.context = context;
}
#Override
protected Void doInBackground(Login... params) {
Log.i(TAG, "doInBackground get refreshing threshold: " + CognitoCachingCredentialsProvider.DEFAULT_THRESHOLD_SECONDS);
mImei = params[0].getImei();
mPassword = params[0].getPassword();
Login login = params[0];
// if(mPassword == null){
// Log.i(TAG, "register");
// mPassword = Utils.generateRandomString();
// final Register register = new Register();
// register.setImei(mImei);
// register.setPassword(mPassword);
// login.setPassword(mPassword);
// RegisterResponse registerResponse = AuthenticationProvider.getSocializeClientInstance().socializeAuthRegisterPost(register);
// Log.i(TAG, "registerResponse: " + registerResponse.getCreated());
UserSingleton.password = mPassword;
UserSingleton.getInstance().saveRegistrationInfo();
Log.i(TAG, "imei: " + mImei);
// }
Log.i(TAG, "calling login post");
LoginReponse loginReponse = AuthenticationProvider.getSocializeClientInstance().socializeAuthLoginPost(login);
Log.i(TAG, "login response: " + loginReponse.getIdentityId() + " - token: " + loginReponse.getToken());
// Set up the loginsMap to send with the credentials request
Map<String, String> loginsMap = new HashMap<>();
loginsMap.put(CognitoSyncClientManager.developerIdentityProvider.getProviderName(), loginReponse.getToken());
// get AWS credentials to access DB
GetCredentialsForIdentityRequest credentialsForIdentityRequest = new GetCredentialsForIdentityRequest();
credentialsForIdentityRequest.setIdentityId(loginReponse.getIdentityId());
credentialsForIdentityRequest.setLogins(loginsMap);
Log.i(TAG, "credentials request: " + credentialsForIdentityRequest.getIdentityId() + credentialsForIdentityRequest.getLogins());
AmazonCognitoIdentityClient cognitoIdentityClient = new AmazonCognitoIdentityClient(CognitoSyncClientManager.credentialsProvider);
credentialsForIdentityResult = cognitoIdentityClient
.getCredentialsForIdentity(credentialsForIdentityRequest);
isSuccessful = credentialsForIdentityResult != null;
return null;
}
#Override
protected void onPostExecute(Void result) {
if (isSuccessful) {
Log.i(TAG, "accessKeyId: " + credentialsForIdentityResult.getCredentials().getAccessKeyId()
+ "\nsecretKey: " + credentialsForIdentityResult.getCredentials().getSecretKey()
+ "\nsessionToken: " + credentialsForIdentityResult.getCredentials().getSessionToken());
CognitoSyncClientManager
.addLogins(
((AuthenticationProvider) CognitoSyncClientManager.credentialsProvider
.getIdentityProvider()).getProviderName(),
mImei);
} else {
Log.i(TAG, "login error: " + result);
}
}
}
In my refresh call I am just relogging in. I'm not sure if that is correct.
The biggest issue now is upon startup when I attempt to login using, mSocializeClient.socializeAuthLoginPost(login) it seems to call refresh every time, before it even logs in. Refresh then attempts to login again and it keeps calling itself endlessly.
Any help/explanations would be greatly appreciated.
The way the dev auth sample works is as follows:
The DeveloperAuthenticationTask.login() is supposed to login to the server and get a session key.
It sets up the logins map and calls DeveloperAuthenticationProvider.refresh()
Refresh exchanges the session key with the server for a valid cognito token and identity id and calls update with the token and identity id.
In your case, you don't have this session key, just username and password. So you don't need the AuthenticationTask. All you need is:
a login() in your AuthenticationProvider, that puts the username/password in a secure location, sets up the login map and calls refresh (it shouldn't actually attempt to login to your service).
In refresh() you retrieve the username/pass from the secure location, call your service and then call update with the token and identity id returned from your service.
Can you simplify your code to this flow?
I am working with authenticating via Google+ according to the following:
https://developers.google.com/+/mobile/android/sign-in
Most of this process seems fine. The problem I'm having is that we need to get a "one-time authorization code" so that our backend servers can perform certain requests on behalf of the user, with their permission. This is covered in the section "Enable server-side api access for your app". However, for a number of reasons, our servers can cause the login to fail, even if the authorization code is valid (e.g. the user doesn't have an account corresponding to the google+ account on our servers yet, in which case they can make one).
If this happens, we might need them to login again at a later time. What I'm finding, though, is that when I perform the second login with google+, it gives me the same authorization code, even if it's already been used by our servers. I've tried disconnecting and reconnecting to the google client api, and calling GoogleApiClient.clearDefaultAccountAndReconnect(), but no matter what I do, I seem to end up with the same authorization code. This, of course, is rejected by the server when it tries to use it, since it's already been used.
I'm wondering what I'm doing wrong here. I have the following method, which is called during the initial authentication process, and then again if a response status of 500 is detected from our server (indicating the previous call failed, presumably because the code has already been used):
private void dispatchGooglePlusAuthCodeAcquisition() {
AsyncTask<Void, Void, String> authAcquisition = new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
Bundle authPreferences = new Bundle();
mUserPermissionNeededForAuthCode = false;
authPreferences.putString(GoogleAuthUtil.KEY_REQUEST_VISIBLE_ACTIVITIES,
"");
String scopesString = Scopes.PROFILE;
WhenIWorkApplication app = (WhenIWorkApplication)WhenIWorkApplication.getInstance();
String serverClientID = app.getGoogleOAuthClientIDForPersonalServer();
String scope = "oauth2:server:client_id:" + serverClientID + ":api_scope:" + scopesString;
String code = null;
authPreferences.putBoolean(GoogleAuthUtil.KEY_SUPPRESS_PROGRESS_SCREEN, true);
try {
code = GoogleAuthUtil.getToken(
mActivity,
Plus.AccountApi.getAccountName(mGoogleApiClient),
scope,
authPreferences
);
} catch (IOException transientEx) {
// network or server error, the call is expected to succeed if you try again later.
// Don't attempt to call again immediately - the request is likely to
// fail, you'll hit quotas or back-off.
Log.d(LOGTAG, "Encountered an IOException while trying to login to Google+."
+ " We'll need to try again at a later time.");
} catch (UserRecoverableAuthException e) {
mUserPermissionNeededForAuthCode = true;
// Requesting an authorization code will always throw
// UserRecoverableAuthException on the first call to GoogleAuthUtil.getToken
// because the user must consent to offline access to their data. After
// consent is granted control is returned to your activity in onActivityResult
// and the second call to GoogleAuthUtil.getToken will succeed.
if (!mGooglePlusPermissionActivityStarted) {
mGooglePlusPermissionActivityStarted = true;
mActivity.startActivityForResult(e.getIntent(), RESULT_CODE_AUTH_CODE);
}
} catch (GoogleAuthException authEx) {
// Failure. The call is not expected to ever succeed so it should not be
// retried.
Log.e(LOGTAG, "Unable to authenticate to Google+. Call will likely never"
+ " succeed, so bailing.", authEx);
}
return code;
}
#Override
protected void onPostExecute(String aResult) {
if (aResult != null) {
// We retrieved an authorization code successfully.
if (mAPIAccessListener != null) {
mAPIAccessListener.onAuthorizationCodeGranted(aResult);
}
} else if (!mUserPermissionNeededForAuthCode) {
// If this is the case, then we didn't get authorization from the user, or something
// else happened.
if (mAPIAccessListener != null) {
mAPIAccessListener.onAuthorizationFailed();
}
Log.d(LOGTAG, "Unable to login because authorization code retrieved was null");
}
}
};
authAcquisition.execute();
So, the answer to this was a lot simpler than I imagined. Apparently, there is aclearToken() method on the GoogleAuthUtil class:
http://developer.android.com/reference/com/google/android/gms/auth/GoogleAuthUtil.html#clearToken%28android.content.Context,%20java.lang.String%29
public static void clearToken (Context context, String token)
Clear the specified token in local cache with respect to the Context. Note that the context must be the same as that used to initialize the token in a previous call to getToken(Context, String, String) or getToken(Context, String, String, Bundle).
Parameters
context Context of the token.
token The token to clear.
Throws
GooglePlayServicesAvailabilityException
GoogleAuthException
IOException
Calling this method before attempting to re-authenticate causes Google to generate a new one-time authorization token.