Google+ signout without dialog popup - google-plus

I have a signout button on my page that I'm initiating this way:
$('#logout').click(function() {
gapi.auth.signIn({
'callback': function(authResult) {
if (authResult['status']['signed_in']) {
gapi.auth.signOut();
} else {
// second pass, signout succesful
}
}
})
});
This ends up making two calls out to Google (first to validate that user is already logged in, second to sign out), thus the two passes through the callback code. This also causes the Google+ login window to briefly popup.
Is there a way to just call gapi.auth.signOut() directly without the signIn step? I have the user's Google+ id (and also access_token), if that helps.

You don't need to call the gapi.auth.signIn() every time you want to sign out. just call the gapi.auth.signOut() from anywhere to initiate the sign-out process from your app (but still signed in to Google in other tabs, which is good practice).
Example would be to just attach the gapi.auth.signOut() event to an onclick event on a button;
<button onclick="gapi.auth.signOut();">Sign out</button>

Related

Vue,js used with Supabase - can't update signIn button after logging in with Oauth

async handleSignInSignOutButtonClick() {
if (!this.isSignedIn) {
supabase.auth.signIn({ provider: "google" });
this.$store.commit("signIn", supabase.auth.session());
window.location.reload();
return;
}
await this.$store.commit("signOut");
supabase.auth.signOut();
window.location.reload();
},
The above function is triggered by a sign-in button, which is supposed to become a sign-out button and the icon of the user after logging in.
When The function fires, supabase redirects me to Google OAuth consent screen. However, after logging in and redirecting back to my app, the sign-in button stays there until I manually refresh the page.
What is wrong with my code...
There are a couple of things going on that you need to be aware of. For starters you are reloading your page when you don't need to in the handleSignInSignOutButtonClick() function.
When the authentication process begins, your app will be redirected to Google OAuth consent screen as you have discovered. Once the authentication is complete, you will be redirected back to your app and the reload occurs automatically.
The second point is that you can make use of the supabase.auth.onAuthStateChange() event to help you. My suggestion would be to listen for this event when you create your supabase client so it listens for the duration of your app instance. During that event handling, you can assign the user to the store (or anywhere you want to save the user data) based upon the state change. Your app can be reactive to state changes.
In your supabase client setup code:
const supabaseUrl = process.env.SUPABASE_URL // your supabaseUrl
const supabaseAnonKey = process.env.SUPABASE_ANON_KEY // your supabaseKey
const supabase = createClient(supabaseUrl, supabaseAnonKey)
/**
* Set up the authentication state change listener
*
* #param {string} event The event indicates what state changed (SIGN_IN, SIGN_OUT etc)
* #param {object} session The session contains the current user session data or null if there is no user
*/
supabase.auth.onAuthStateChange((event, session) => {
const user = session?.user || null;
// Save your user to your desired location
$store.commit("user", user);
});
Now you have your user data being saved whenever the user logs in and a null being set for the user data when the user logs out. Plus any page refreshes are handled by the change state event listener or any other instance that might change the user state. For example, you could have other login or logout buttons and the single listener would pick them up.
Next is to deal with the actual process of logging in or out. In your component Vue file (from your example):
async handleSignInSignOutButtonClick() {
if ($store.state.user === null) {
await supabase.auth.signIn(
{ provider: "google" },
{ redirectTo: "where_to_go_on_login" }))
} else {
await supabase.auth.signOut()
$router.push("your_logged_out_page")
}
}
Finally for your button change state to indicate logged in or logged out, you can simply observe the store user state.
<button v-if="user">Sign Out</button>
<button v-else>Sign In</button>
This way your button will update whenever the user state changes. The user state changes whenever a user logs in or out, and your code is much more compact and readable.
Once final observation that you may already be doing anyway. I would recommend that you put all of your authentication code into a single file and expose the log in and log out functions for your app use as an export to use in component files. This way everything to do with login and logout is handled in a single location and this code is abstracted away from the component file. If you ever wanted to switch from Supabase you could easily update one or two files and everything else would just keep working.

Google OAuth2 SignIn method fires at page load

I have used the official button for Google SignIn:
SignIn Page:
<div class="g-signin2" data-onsuccess="AuthenticateGoogleUser"></div>
function AuthenticateGoogleUser(googleUser){
.....
capture the user info and redirect to Home page
.....
}
When configuring for the credentials, I have set the redirection URL to the signin page.
This is how the Signout happens for the app:
function SignOutGoogleUser() {
if (gapi != null && gapi != undefined &&
gapi.auth2 != null && gapi.auth2 != undefined) {
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function () {
auth2.disconnect();
....Redirect to Home page...
});
}
}
The methods work fine. If I signout, will be redirected to home page.
But, when I manually browse the SignIn page after signout, the AuthenticateGoogleUser method is triggered and I am auto signed into the app (with google account).
The AuthenticateGoogleUser method should be only triggered on the button click. Is that right.
But here it is being triggered on load of SignIn page. Is that expected. Can this be stopped.
I'm using MVC C# as backend.
Without seeing all of your code, I am working on the assumption that you have not placed the function in a document ready wrapper and made sure to only kick it off on a button click or other event from which you would like to have it fire. As it stands now, it will fire on that page load.
$(document).ready(function(){
$("btnLogin").click(function(){
AuthenticateGoogleUser(googleUser);
});
});

Auto Sign In with Cognito / Amplify Authenticator Components?

I'm using the Amplify authenticator component (described here) with Cognito User Pools.
Right now, adding the basic authenticator to html takes the user through the following process automatically:
1) Sign up
2) Enter verification code sent to email
3) Sign-in: Re-enter user name and password
This is based on just adding to html:
<amplify-authenticator></amplify-authenticator>
So new users sign up, and then right away need to sign in. It would be better if they were automatically signed-in, so that upon entering their verification code they went right into the app. This would be a common authentication flow.
Is there a way to have automatic sign-in like this while still using the authenticator components?
I see a github discussion about this topic here, but it is not resolved.
I have figured it out for my purposes. The key is that the Amplify components (authenticator, as well as the individual components) give out information about the state after the user takes action. You can listen for this info, and then programmatically sign in the user once you get the "signIn" state.
These are the states, as described here (I am using Angular):
'signUp' //when you want to show signUp page
'confirmSignUp' //the user has probably just submitted info for signing up, and now you want to show the "confirm" page--like the page where they can enter a verification code
'signIn' //this is the key for these purposes. This is when you want to show the sign in page. In the basic authenticator structure, this happens just after the user has been signed up. So this is where you can automatically sign in the user with Auth.signIn, like below.
'confirmSignIn' //I assume this is after user has submitted the sign in form and you want a confirm page to show
'signedIn' //presumably after user has been fully signed in (not confirmed)
'forgotPassword' //presumably when the forgotPassword form loads (not confirmed)
'requireNewPassword' //presumably when user clicks 'reset password' (not confirmed)
So, using Authenticator, you can listen for the 'signIn' event and automatically sign in the user then. Note however that, while this will sign in the user automatically, Authenticator may still show the sign in page while this action is being processed. You can hide this page, or just use the individual Amplify components, like I show in #2 below.
Even better would be if Amplify/Cognito could just have an option for the developer to select automatic sign in after sign up when they set up Cognito. Automatic sign in on sign up would be in line with modern authentication practice, and would be better for user flow. I do not find this option anywhere. If anyone at Amplify/Cognito is listening, please consider adding this feature.
Note: There seem to be some issues that can arise when using these components. For example--with the authenticator, if the user submits info to sign up, but then leaves the page before entering their verification code: the next time they try to access the app they may have trouble, because they can't go through the sign up process again (a user has already been created), but they also can't sign in (because they've never verified). Some more discussion on that is here.
Additionally, I have found error messages to be an issue. These Amplify components automatically show error messages, and some of them from Cognito are technical messages I would never want a user to see (stuff that talks about "user id" and lambda functions, depending on the random error). There may be a way to customize these, like described here, but be sure you test a lot of different scenarios to see what might happen in terms of the error messages.
If these issues prove problematic for you (like they have for me), you may want to use your own forms instead, and then use Auth.signIn() and related Amplify methods, instead of these components.
But, for auto-signing-in with Amplify components, here is code that worked for me:
1. Using Amplify's Authenticator component:
html
<amplify-authenticator></amplify-authenticator>
ts:
import { AmplifyService } from 'aws-amplify-angular';
import Auth from '#aws-amplify/auth';
Export class AuthComponent implements OnInit {
state: any;
newUser: any;
username: any;
password: any;
constructor(public amplifyService: AmplifyService){
this.amplifyService.setAuthState(this.authState) //may be not required
this.amplifyService.authStateChange$ //listening for state changes
.subscribe(authState => {
this.state = authState.state
if (this.state === 'confirmSignUp'){
console.log('user just signed up, now on verify code form')
this.newUser = authState.user
this.password = this.newUser.username
let checkPassword = this.newUser.username.password
if (checkPassword != 'undefined') {
this.password = checkPassword
/*Note here: I have coded it this way because it looks like the authenticator runs two events when the user hits "sign up". In the first event, you can get the user's password, to be used in the Auth.signIn() function below. In the second event, you can only get the user's username (and password would show up as undefined if you grabbed it there). So we need a way to get the password from the first event.*/
}
}
if ((this.newUser) && (this.state === 'signIn')){//this.newUser included because otherwise this event will fire anytime the sign in page loads--even for returning users trying to sign in (who you would not want to sign in automatically)
console.log('user has just finished signing up)
Auth.signIn(this.username, this.password).then(()=>{//there might be more parameters, like email, first name and last name, phone number, etc. here--depends on your Cognito settings
console.log('should be signed in now! You can navigate away from this page')
}).catch((error)=>{
console.log('error here = ' + error.message + ', error code = ' + error.code)
})
}
})
}
}
Like I mentioned above, this will show the "sign in" page while the Auth.signIn() function is processing. To avoid that, you could have an *ngIf, saying hide the page when this.state = "signIn".
2. Using Individual Amplify Auth Components:
In the below, the page will load with the sign up form.
Once the user enters their details and clicks sign up, the page will show the "confirm sign up" form, which is where the user enters a verification code that Cognito has sent to him/her (depending on your Cognito settings).
Then, once the user is signed up, you can get the "signIn" state like above, and automatically sign the user in:
html:
<amplify-auth-sign-up [authState]="authState" *ngIf="showSignUp"></amplify-auth-sign-up>
<amplify-auth-confirm-sign-up [authState]="authState" *ngIf="showVerify"></amplify-auth-confirm-sign-up>
ts:
import { AmplifyService } from 'aws-amplify-angular';
import { AuthState } from 'aws-amplify-angular/dist/src/providers';
import Auth from '#aws-amplify/auth';
Export class AuthComponent implements OnInit {
public authState: AuthState
public newUser: any
public username: any
public password: any
public state: any
public showSignUp = true
public showVerify = false
constructor(public amplifyService: AmplifyService) {
this.authState ={//the individual Amplify components require a state be set
user: null,
state: 'signUp'
}
this.amplifyService.setAuthState(this.authState) //this might not be required
this.amplifyService.authStateChange$ //listening for state changes.
.subscribe(authState => {
this.state = authState.state
if (this.state === 'confirmSignUp'){//get change in state
this.newUser = authState.user
this.username = this.newUser.username
let checkPassword = this.newUser.username.password
if (checkPassword != 'undefined') {
this.password = checkPassword
/*Note here: I have coded it this way because it looks like the authenticator runs two events when the user hits "sign up". In the first event, you can get the user's password, to be used in the Auth.signIn() function below. In the second event, you can only get the user's username (and password would show up as undefined if you grabbed it there). So we need a way to get the password from the first event.*/
}
this.authState ={
user: authState.user,
state: 'confirmSignUp'
}
this.showSignUp = false
this.showVerify = true
}
if ((this.newUser) && (this.state === 'signIn')){//this.newUser included because otherwise this event will fire anytime the sign in page loads--even for returning users trying to sign in (who you would not want to sign in automatically)
console.log('user has just finished signing up)
Auth.signIn(this.username, this.password).then(()=>{//there might be more parameters, like email, first name and last name, phone number, etc. here--depends on your Cognito settings
console.log('should be signed in now! You can navigate away from this page')
}).catch((error)=>{
console.log('error here = ' + error.message + ', error code = ' + error.code)
})
}
})
}
}

Ionic 3 app - allow navigation to page if user logged (check on one place, rather on every component)

How can I add check whether user is logged or not and in accordance with that, allow navigation to desired page or not? It wouldn't be good practice to add :
ionViewCanEnter() {
return this.auth.isAuthenticated();
}
check at the top of every component...
I recommend using an authentication token for your user login. This will allow you to locally store as a variable or local storage and you can implement in your service or provider to be used throughout the app. If you're uncertain with how they work there are plenty of resources online, but ultimately it comes down to your back-end server. Here's an example:Auth Token Example
Also, I would recommend you use *ngIf statement blocks in your html pages where the buttons navigate to the pages themselves and throw an alert if the user tries clicking on the button to navigate.
I have some sample code that can help guide you with this as well.
LoginPage.ts
// API POST authentication
this.API.validateUser(form.value).then((result) =>{
form.reset();//clears values of the form after data is saved to array
this.res = JSON.parse(result.toString());//converts result to array
//console.log(this.res);
if(this.res.token!=""){//sets authtoken to local storage
this.storage.set('authToken',this.res.token)
}
//console.log(localStorage);
if(this.res.status == true){
setTimeout(() => {
LoginPage.initialLogin = true;
this.navCtrl.push(MenuPage);
loading.dismiss();
}, 1000);
}
MenuPage.ts
// MenuPage.ts
/* calls local storage once user hits menupage*/
if(LoginPage.initialLogin==true){
//console.log('Initial Login is:',LoginPage.initialLogin);
this.storage.get('authToken').then((data)=>{//grabs local storage auth token
if(data!=null){
//console.log('GET request happened');
this.loggedIn = true;//User is logged in
this.reap.grabAPIData(data);//calls service to grab API data on initial login
}
});
}
else{
this.reap.getLocalStorage();
//console.log('Initial Login is:',LoginPage.initialLogin);
}
MenuPage.html
This is where you can use your value to determine what the user can see or not see. The button can be hidden or you can throw an alert in the .ts file that lets user know they aren't logged in.
<ion-item *ngIf="loggedIn" no-lines>
<button class="menuButton" ion-button large block (tap)="toNexPage()" >
Next page</button>
</ion-item>

How to detect `getRedirectResult()` state after `signInWithRedirect()`

I'm using signInWithRedirect(provider), and getRedirectResult() to fetch the result after sign-in from provider (i.e. Google).
The problem I am facing is that, it seems getRedirectResult() took a long time (could take 3-5 sec) to resolve after redirection, and as a result, it's still showing the default view (i.e. sign-in form) after Google redirect, while waiting for the promise to resolve, which is not a great experience.
Is there a way to detect if the user is redirected from Google Sign-in, so that I can load up a spinner, while waiting for getRedirectResult() to resolve?
I have tried document.referrer with no luck.
getRedirectResult() should resolve quickly (fraction of a second) when there is no pending redirect. If there is a pending redirect, you should show some spinner before it resolves. BTW, you can also use onAuthStateChanged to detect the sign in state too regardless there is a pending redirect or not.
Here is a simple example of how to show a spinner. while the redirect operation is being processed.
// On load, show spinner.
showSpinner();
firebase.auth().getRedirectResult().then(result => {
// If user just signed in or already signed in, hide spinner.
if (result.user || firebase.auth().currentUser) {
hideSpinner();
} else {
hideSpinner();
showSignInForm();
}
});
You can also track it yourself.
// Before starting sign in with redirect.
window.sessionStorage.setItem('pending', 1);
firebase.auth().signInWithRedirect(authProvider)...
// On return.
if (window.sessionStorage.getItem('pending')) {
window.sessionStorage.removeItem('pending');
showSpinner();
firebase.auth().getRedirectResult().then(result => {
hideSpinner();
});
}