So here is the code i used to sign my user into cognito (i hope im correct). Now, how would i sign out? Currently i have my own signing up process (so no facebook or google yet).
// Callback handler for the sign-in process
private AuthenticationHandler authenticationHandler = new AuthenticationHandler()
{
#Override
public void onSuccess(CognitoUserSession userSession, CognitoDevice newDevice)
{
Log.d(COGNITO_LOGIN,"Login success!");
cognitoUser.getDetailsInBackground(getDetailsHandler);
//Now we get user from dynamoDB and store it into a local user object.
}
#Override
public void getAuthenticationDetails(AuthenticationContinuation authenticationContinuation, String userId)
{
Log.d(COGNITO_LOGIN,passwordET.getText().toString());
// The API needs user sign-in credentials to continue
AuthenticationDetails authenticationDetails = new AuthenticationDetails(userId, passwordET.getText().toString(), null);
// Pass the user sign-in credentials to the continuation
authenticationContinuation.setAuthenticationDetails(authenticationDetails);
// Allow the sign-in to continue
authenticationContinuation.continueTask();
}
#Override
public void getMFACode(MultiFactorAuthenticationContinuation multiFactorAuthenticationContinuation) {
// Multi-factor authentication is required; get the verification code from user
multiFactorAuthenticationContinuation.setMfaCode("verificationCode");
// Allow the sign-in process to continue
multiFactorAuthenticationContinuation.continueTask();
}
#Override
public void authenticationChallenge(ChallengeContinuation continuation) {
}
#Override
public void onFailure(Exception exception)
{
// Sign-in failed, check exception for the cause
Log.d(COGNITO_LOGIN,"Login failed!");
Log.d(COGNITO_LOGIN,exception.getMessage());
}
};
cognitoUser.getSessionInBackground(authenticationHandler);
You should be able to just call signOut on a cognitoUser object such as below. What that does is clear access, id and refresh tokens from the device so you would need to authenticate again.
// This has cleared all tokens and this user will have to go through the authentication process to get tokens.
user.signOut();
There is also a globalSignOut call that revokes tokens server-side.
There is a way to wipe or clear the session for the current user who logged, the following is the way, which I found so far.
This is for fb in federated identities
if (fbAccessToken != null) {
LoginManager.getInstance().logOut();
}
This is for twiiter
if (mAuthManager != null) {
mAuthManager.clearAuthorizationState(null);
}
// wipe data
CognitoSyncClientManager.getInstance()
.wipeData();
CognitoUserPool pool = AwsCognitoHelper.getPool();
if (pool != null) {
CognitoUser user = pool.getCurrentUser();
if (user != null) {
GenericHandler handler = new GenericHandler() {
#Override
public void onSuccess() {
}
#Override
public void onFailure(Exception e) {
}
};
user.globalSignOutInBackground(handler);
}
}
Related
I am integrating twitter login in my app. I am able to get session and access token successfully.Its also giving me email, user name. But I need user first name ,last name and user image.How can I get user profile details.I have written following code:
ivTwitterLogin.setOnClickListener(v -> {
mTwitterAuthClient.authorize(getActivity(), new com.twitter.sdk.android.core.Callback<TwitterSession>() {
#Override
public void success(Result<TwitterSession> twitterSessionResult) {
TwitterSession session =twitterSessionResult.data;
}
#Override
public void failure(TwitterException e) {
ToastUtils.showToastShort(context, "Login failed");
e.printStackTrace();
}
});
});
You can retrieve the profile image from AccountService#verifyCredentials which returns a User object.
Call<User> call = Twitter.getApiClient(session)
.getAccountService()
.verifyCredentials(true, false);
call.enqueue(new Callback<User>() {
#Override
public void success(Result<User> result) {
// Do something with user object
}
#Override
public void failure(TwitterException exception) {
}
});
Have you read twitter documentation. Do they allow you to access profile pic? Also sometimes user may have not allowed in his/her settings to share details with anyone.
I'm new to RxJava. I have an Android app that is using AWS Cognito SDK for authentication. I have an AwsAuthClient class that handles calling the SDK and returning the results. I have a fragment that calls the SignUp method in AwsAuthClient. I need to return the results of signup to the fragment so that it can react appropriately.
RegisterFragment class:
public class RegisterFragment{
AwsAuthClient authClient;
public void onCreateAccountClick() {
Subscription createSubscription = authClient.SignUp(params)
.compose(Transformers.applyIoToMainSchedulers())
.subscribe((CognitoUser currentUser) -> {
transitionToVerificationScreen();
}, (Throwable throwable) -> {
// Report the error.
});
}
}
Here is the AwsAuthClient:
public class AwsAuthClient {
public void SignUp(CreateParams createParams){
// Create a CognitoUserAttributes object and add user attributes
CognitoUserAttributes userAttributes = new CognitoUserAttributes();
// Add the user attributes. Attributes are added as key-value pairs
// Adding user's given name.
// Note that the key is "given_name" which is the OIDC claim for given name
userAttributes.addAttribute("given_name", createParams.getFirstname() + " " + createParams.getLastname());
// Adding user's phone number
userAttributes.addAttribute("phone_number", createParams.getPhone());
// Adding user's email address
userAttributes.addAttribute("email", createParams.getPhone());
SignUpHandler signupCallback = new SignUpHandler() {
#Override
public void onSuccess(CognitoUser cognitoUser, boolean userConfirmed, CognitoUserCodeDeliveryDetails cognitoUserCodeDeliveryDetails) {
// Sign-up was successful
currentUser = cognitoUser;
// Check if this user (cognitoUser) needs to be confirmed
if(!userConfirmed) {
// This user must be confirmed and a confirmation code was sent to the user
// cognitoUserCodeDeliveryDetails will indicate where the confirmation code was sent
// Get the confirmation code from user
Timber.d("Sent confirmation code");
}
else {
// The user has already been confirmed
Timber.d("User has already been confirmed.");
}
}
#Override
public void onFailure(Exception exception) {
// Sign-up failed, check exception for the cause
}
};
userPool.signUpInBackground(userId, password, userAttributes, null, signupCallback);
}
}
How can I return the results of onSuccess or OnFailure up to the RegisterFragment class?
It looks like the Cognito SDK already provides an async way to get information. In order for you to wrap this into an rx stream, you should consider using a Subject.
Subject are both Observables capable of emitting data, and Observers capable of receiving data. A Subject can wait to receive the callback data, take the data, and then emit it onto a stream.
public Observable<CognitoUser> SignUp(CreateParams createParams){
BehaviorSubject<CognitoUser> subject = BehaviorSubject.create();
// ...
SignUpHandler signupCallback = new SignUpHandler() {
#Override
public void onSuccess(CognitoUser cognitoUser, boolean userConfirmed, CognitoUserCodeDeliveryDetails cognitoUserCodeDeliveryDetails) {
// Sign-up was successful
// Check if this user (cognitoUser) needs to be confirmed
if(!userConfirmed) {
// This user must be confirmed and a confirmation code was sent to the user
// cognitoUserCodeDeliveryDetails will indicate where the confirmation code was sent
// Get the confirmation code from user
Timber.d("Sent confirmation code");
}
else {
// The user has already been confirmed
Timber.d("User has already been confirmed.");
}
subject.onNext(cognitoUser);
subject.onComplete();
}
#Override
public void onFailure(Exception exception) {
subject.onError(exception);
}
};
userPool.signUpInBackground(userId, password, userAttributes, null, signupCallback);
return subject;
}
If you are using RxJava2. You can use the create() operator to create your own async call:
public class AwsAuthClient {
public Observable<CognitoUser> SignUp(CreateParams createParams){
return Observable.create(emitter -> {
SignUpHandler signupCallback = new SignUpHandler() {
#Override
public void onSuccess(CognitoUser cognitoUser, boolean userConfirmed, CognitoUserCodeDeliveryDetails cognitoUserCodeDeliveryDetails) {
// Sign-up was successful
emitter.onNext(cognitoUser);
// Check if this user (cognitoUser) needs to be confirmed
if(!userConfirmed) {
// This user must be confirmed and a confirmation code was sent to the user
// cognitoUserCodeDeliveryDetails will indicate where the confirmation code was sent
// Get the confirmation code from user
Timber.d("Sent confirmation code");
}
else {
// The user has already been confirmed
Timber.d("User has already been confirmed.");
}
emitter.onComplete();
}
#Override
public void onFailure(Exception exception) {
// Sign-up failed, check exception for the cause
emitter.onError(exception);
}
};
//cancel the call
Observable.setCancellable(//your cancel code)
})
}
Edit: If you are using RxJava1(latest version 1.3.2) you can just use Observable.create(Action1>,BackPressureMode) instead of create and it's safe
Observable.create(new Action1<Emitter<CognitoUser extends Object>>() {
#Override
public void call(Emitter<CognitoUser> emitter) {
SignUpHandler signupCallback = new SignUpHandler() {
#Override
public void onSuccess(CognitoUser cognitoUser, boolean userConfirmed, CognitoUserCodeDeliveryDetails cognitoUserCodeDeliveryDetails) {
if (!userConfirmed) {
Timber.d("Sent confirmation code");
} else {
Timber.d("User has already been confirmed.");
}
emitter.onNext(cognitoUser);
emitter.onComplete();
}
#Override
public void onFailure(Exception exception) {
emitter.onError(exception);
}
};
emitter.setCancellation(new Cancellable() {
#Override
public void cancel() throws Exception {
//Your Cancellation
}
});
signUpInBackground(userId, password, userAttributes, null, signupCallback);
}
//Because RxJava 1 doesn't have Flowable so you need add backpressure by default.
}, Emitter.BackpressureMode.NONE );
I want to publish post to user's wall. I know Facebook only allows tester and developers to post on wall. I have already added user to tester list. When I try to get publish permission, it says that user has already granted permission (as shown in screenshot) and returns. I am not able to get permission or post on wall. Moreover, callback's any method is not called as well.
CODE
I have followed code from Facebook Example RPSSample.
//Publish to wall
public void publishResult() {
registerPublishPermissionCallback();
if (canPublish()) { //see definition below
ShareLinkContent content = new ShareLinkContent.Builder()
.setContentUrl(Uri.parse(urlToPost))
.build();
ShareApi.share(content, new FacebookCallback<Sharer.Result>() {
#Override
public void onSuccess(Sharer.Result result) {
callback.didShareOnFacebookSuccessfully();
}
#Override
public void onCancel() {
// This should not happen
}
#Override
public void onError(FacebookException error) {
showToast(error.getMessage());
}
});
}
}
//check if user has permission or not
private boolean canPublish() {
final AccessToken accessToken = AccessToken.getCurrentAccessToken();
if (accessToken != null) {
if (accessToken.getPermissions().contains(AppConstants.ADDITIONAL_PERMISSIONS)) {
// if we already have publish permissions, then go ahead and publish
return true;
} else {
// otherwise we ask the user if they'd like to publish to facebook
new AlertDialog.Builder(activity)
.setTitle(R.string.share_with_friends_title)
.setMessage(urlToPost)
.setPositiveButton(R.string.share_with_friends_yes, canPublishClickListener)
.setNegativeButton(R.string.share_with_friends_no, dontPublishClickListener)
.show();
return false;
}
}
return false;
}
//If user allows, ask Facebook to grant publish_action permission
private DialogInterface.OnClickListener canPublishClickListener = new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
if (AccessToken.getCurrentAccessToken() != null) {
// if they choose to publish, then we request for publish permissions
LoginManager.getInstance()
.setDefaultAudience(DefaultAudience.FRIENDS)
.logInWithPublishPermissions(activity,
Arrays.asList(AppConstants.ADDITIONAL_PERMISSIONS));
}
}
};
//Callback - Any of the method doesn't call.
private void registerPublishPermissionCallback() {
LoginManager.getInstance().registerCallback(
callbackManager,
new FacebookCallback<LoginResult>() {
#Override
public void onSuccess(LoginResult loginResult) {
AccessToken accessToken = AccessToken.getCurrentAccessToken();
if (accessToken.getPermissions().contains(AppConstants.ADDITIONAL_PERMISSIONS)) {
publishResult();
} else {
handleError("Not enough permissions to publish");
}
}
#Override
public void onCancel() {
}
#Override
public void onError(FacebookException exception) {
handleError(exception.getMessage());
}
private void handleError(String errorMessage) {
// this means the user did not grant us write permissions, so
// we don't do implicit publishes
showToast(errorMessage);
}
}
);
}
My app is live on console. Please guide what is requirement of Facebook to get publish permission with test user? Thanks.
Use below code to check if permission is Granted or not.
String url = "/" + "(userFbID)" + "/permissions";
new GraphRequest(AccessToken.getCurrentAccessToken(), url, null,
HttpMethod.GET, new GraphRequest.Callback() {
public void onCompleted(GraphResponse response) {
try {
JSONObject json = new JSONObject(
response.getRawResponse());
JSONArray data = json.getJSONArray("data");
boolean isPermitted = false;
for (int i = 0; i < data.length(); i++) {
if (data.getJSONObject(i)
.getString("permission")
.equals("publish_actions")) {
isPermitted = true;
String status = data.getJSONObject(i)
.getString("status");
if (status.equals("granted")) {
publishResult()
} else {
LoginFacebook();
}
break;
}
}
if (!isPermitted) {
LoginFacebook();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).executeAsync();
replace user's fb id on place of (userFbID).
If permission is not granted then use this function to ask user for that permission.
private void LoginFacebook() {
loginManager = LoginManager.getInstance();
loginManager.logInWithPublishPermissions(this,
Arrays.asList("publish_actions"));
loginManager.registerCallback(callbackManager,
new FacebookCallback<LoginResult>() {
#Override
public void onSuccess(LoginResult loginResult) {
publishResult()
}
#Override
public void onError(FacebookException error) {
}
#Override
public void onCancel() {
}
});
}
Finally able to solve the problem. There was no issue in code. It was some setting / permission issue with Facebook app. These are steps I followed:
Remove the user from Test role section in your Facebook Developer Console.
Revoke App's Access by logging into that user's account. Go to Settings > Apps. Remove the app from there. You need to wait 10-15 min, since it takes time from Facebook side to revoke access completely.
Make your Facebook App in Sandbox mode (you need to do this to perform publish testing).
Login your android app using any Facebook account other than app admin. It should throw error that app is in development mode.
Add user as Tester in the Facebook Console App. That user may need to approve pending request at his side.
Login from that user account. Facebook will now ask you to provide basic information access to the app. After that you can test publishing. You will be able to post on the user's wall successfully.
Hope this helps someone!
I decided to use Volley and go the RESTful route with Firebase since their listeners seem to hang when there's no internet connection. At least with Volley, it lets me know if a network request was not successful due to internet connection or not.
I need to know whether FirebaseUser auth tokens expire or not. In my app, I only allow Google and Facebook authentication, and I use the following code assuming that Firebase user auth token DO NOT expire:
private String authToken;
// Callbacks
public interface ApiCallbacks {
public void onSuccess(JSONObject response);
public void onError(String errorString);
}
private interface AuthTokenCallbacks {
public void onAuthTokenSuccess();
public void onAuthTokenError(String errorString);
}
// Request Helpers
private void getAuthToken(final AuthTokenCallbacks callbacks) {
// Lazy instantiation
if (authToken == null) {
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user == null) {
callbacks.onAuthTokenError("Please log in");
} else {
user.getToken(false).addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
#Override
public void onComplete(#NonNull Task<GetTokenResult> task) {
if (task.isSuccessful()) {
authToken = task.getResult().getToken();
callbacks.onAuthTokenSuccess();
} else {
callbacks.onAuthTokenError("Please log in");
}
}
});
}
} else {
callbacks.onAuthTokenSuccess();
}
}
public void sendGetRequestTo(final String endpoint, final HashMap<String, String> arguments, final RequestQueue requestQueue, final String tag, final ApiCallbacks callbacks) {
// Only execute get request if we have an auth token
getAuthToken(new AuthTokenCallbacks() {
#Override
public void onAuthTokenSuccess() {
final String requestUrl = getRequestUrlString(endpoint, arguments);
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, requestUrl, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
callbacks.onSuccess(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
callbacks.onError(error.toString());
}
});
request.setTag(tag);
requestQueue.add(request);
}
#Override
public void onAuthTokenError(String errorString) {
callbacks.onError("Please log in");
}
});
}
Is this the correct way of doing it? I just need to know if I'm going in the right direction because I don't want future problems with my auth tokens expiring (if they do).
EDIT
I forgot to mention that my final String requestUrl = getRequestUrlString(endpoint, arguments); method basically constructs the url request string with auth=authTokenString appended at the end of my url.
Yes, they do expire (you can check out the expiration date at jwt.io). If you don't force a refresh (i.e. user.getToken(false)), the returned token will be updated only if it has expired. If you pass true to getToken(...), a new token will be created which also involves the linked providers' token validation on the firebase servers (e.g. validating against Facebook whether the user still has his/her account linked). Note that the latter counts towards your daily token service quotas, so make sure you use it only when it's necessary.
I have integrated google and facebook login as well as simple login using their respective methods. But after successful signing, I want to manage session of login and logout that means at a time only one login type can use. If user already login with one of them then other login methods are disable. After logout user can login from one of the three methods.
I try to manage session using shared preference but it works only for simple login not for facebook and google login. Is there any other method for session management in android.
Please help, Thanks in advance.
Their is no concept of Session management in Android.
You have to save login status in SharedPreference or Database when user login success.
For sharedPreference refer Store login data in android
**FACEBOOK**
To get an facebook active session call following code in your function
void isFacebookActiveSession(){
Log.d("FacebookLogin"," login from Fb");
facebookSession = Session.getActiveSession();
Log.d("FacebookLogin"," Fb session = "+facebookSession);
facebookSession.openActiveSession(this, true, statusCallback);
}
and implement callback like.
Session.StatusCallback statusCallback = new Session.StatusCallback() {
#Override
public void call(final Session session, SessionState sessionState, Exception e) {
if (session.getPermissions().size() > 0) {
Request request = Request.newMeRequest(session,
new Request.GraphUserCallback() {
#Override
public void onCompleted(GraphUser user, Response response) {
// If the response is successful
if (session == Session.getActiveSession()) {
if (user != null) {
// use is login and jump to another activity or do whatever your requiements
}else{
// login page
}
}
}
});
request.executeAsync();
}
else {
if (facebookSession != null)
facebookSession.close();
}
}
};
To check google plus login status.
googleClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(Bundle bundle) {
googleClient.disconnect();
// already log in jump to another activity
}
#Override
public void onConnectionSuspended(int i) {
if(loginAttempts < MAXIMUM_ATTEMPTS) {
googleClient.connect();
loginAttempts++; // try to reattempt
}
}
})
.addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
// Not logged in redirect login page
googleClient.disconnect();
}
})
.addApi(Plus.API)
.addScope(Plus.SCOPE_PLUS_LOGIN)
.build();
googleClient.connect();