Google Login fails: either I get an error about an unlisted "redirect_uri" value or I get redirected to localhost which also fails - authentication

I am trying to enable users to log into my page using Google Social Sign On.
This worked in development and now I am going to production with it. It is a resume project.
To start the login process I click an anchor tag which redirects me to https://www.example.com/api/auth/google which Nginx sends to my backend endpoint: this.router.get("/google", googleAuth); which performs some magic to redirect the user to a page that formerly showed my 3 Google accounts.
This page fails and says "Access blocked: This app’s request is invalid"
I click to see error details and get
Error 400: redirect_uri_mismatch
You can't sign in to this app because it doesn't comply with Google's OAuth 2.0 policy.
If you're the app developer, register the redirect URI in the Google Cloud Console.
Request details: redirect_uri=http://localhost:8000/auth/google/callback
I am expecting it to redirect to https://www.example.com/api/auth/google/callback
In my Google console for this application I click under OAuth 2.0 Client IDs and see that my "Authorized redirect URIs" lists one singular entry where I expect to look and see "http://localhost:8000/auth/google/callback" : but it says https://www.example.com/api/auth/google/callback
It's like OK why not add the localhost:8000 value to the redirect URIs? Ok let's try it
Now I try clicking the Google button, and get redirected to a list of my available google accounts as I want. But now when I click one of them to be logged in, I get redirected to localhost, which is not what I want, I want my production URL:
This site can’t be reached
localhost refused to connect.
So what I want is for the redirect to the login page to come from my "example.com/api/auth/google" URL, and for the redirect to be sent to "example.com.api/auth/google/callback"
How can I make this happen? I think it requires nginx configured somehow.
edit to include code:
Here are the relevant controller endpoints
this.router.get("/google", googleAuth);
this.router.get("/google/callback", googleAuthCallback, this.grantAccessToken.bind(this));
googleAuth and googleAuthCallback:
export const googleAuth = passport.authenticate("google", { scope: ["email", "profile"], successRedirect: "http://localhost:3000" });
export const googleAuthCallback = passport.authenticate("google", { session: false });
My client side code that starts the redirect:
const GoogleButton: React.FC<{}> = () => {
const startGoogleSignInFlow = getEndpoint("/auth/google");
// the value is https://www.apartmentsneargyms.com/api/auth/google
return (
<a href={startGoogleSignInFlow} className="flex items-center">
<div className="w-full h-12 flex justify-center rounded-lg googleBtn">
<button className="flex items-center mr-4">
<img alt="google icon" src={GoogleIcon} height={24} width={24} className="mr-2" />
Google
</button>
</div>
</a>
);
};
I almost exclusively followed this guide to get here.
My nginx configuration proxies www.example.com/api to the backend server like this:
location /api/ {
proxy_pass http://localhost:8000/;
}
I'm suspicious the problem is that the Google services are hearing from me in the form of a request that starts with localhost:8000 because the redirects come from that location on my remote machine's network.
edit2: I want to say that I had this working on my local machine using localhost. It's deploying to prod that's difficult here.

Found the source of the problem. Linda Lawton - DaImTo was right to tell me to post my code. I found this in my passport config:
const passportConfig = (passport: any) => {
passport.use(
new GoogleStrategy(
{
clientID: googleClientId,
clientSecret: googleClientSecret,
callbackURL: "http://localhost:8000/auth/google/callback",
passReqToCallback: true,
},
// snip
}

On the following line:
export const googleAuth = passport.authenticate("google", { scope: ["email", "profile"], successRedirect: "http://localhost:3000" });
You've your local env URL hardcoded (http://localhost:3000), this value must be different in dev and production.

Related

Sign-in with Google button in a microservice-based architecture

I have a microservice-based application that implements a dedicated user authentication and authorization service. This service has two API endpoints to login with Google: GET signin and POST callback. If GET signin is called, it will challenge the user by redirecting them to Google login, and after they sign in to Google, Google calls the POST callback API. The POST callback will sign in the user using the ID and access token it receives from Google, creates a local user, and returns its DTO.
I am trying to implement the Sign in with Google button in the UI. I am following Google's documentation on creating this button. Here is what I have so far.
<html>
<body>
<script src="https://accounts.google.com/gsi/client" async defer></script>
<script>
window.onload = function () {
google.accounts.id.initialize({
client_id: "...",
login_uri: "https://localhost:1234/signin",
ux_mode: "redirect"
});
google.accounts.id.renderButton(
document.getElementById("buttonDiv"),
{ theme: "outline", size: "large", click_listener: onClickHandler }
);
function onClickHandler(){
window.location.replace("https://localhost:1234/signin")
}
google.accounts.id.prompt();
}
</script>
<div id="buttonDiv"></div>
</body>
</html>
This setup works, but not as expected.
The interaction I expect compared to what happens:
User clicks on the Login with Google button;
[expect] A pop-up opens and redirects to https://localhost:1234/signin. [happens] full-page redirect to https://localhost:1234/signin;
https://localhost:1234/signin redirects to Google login;
After the user sign in, https://localhost:1234/callback is called, which will sign in the user and redirects to the page where the Sign in with Google button was clicked.
I expect the redirect to happen in the pop-up, so the callback redirect happens in the pop-up, and user does not experience a full-page refresh. However, using the above code, two full-page refreshes happen: (a) redirect to the login endpoint, then to Google, (b) Google to the callback, then to the page where the login button was clicked.

Unable to login to socialite using google one tap signin button

I tried almost everything possible but Google One Tap signin button is returning 504 error in my Laravel 8 application. I am using laravel socialite to handle login process and if i place a link button instead Google Signin Button it works absolutely fine.
Here is my One Tap Button Code :
<div id="g_id_onload" data-client_id="{{ config('services.google.client_id') }}" data-context="signin" data-login_uri="{{ route('frontend.auth.social.login', 'google') }}" data-auto_prompt="true"></div>
<div class="g_id_signin" data-type="standard" data-shape="rectangular" data-theme="filled_blue" data-text="signin_with" data-size="large" data-logo_alignment="left"></div>
this is the error I am getting:
Oops! An Error Occurred
The server returned a "405 Method Not Allowed".
Something is broken. Please let us know what you were doing when this error occurred. We will fix it as soon as possible. Sorry for any inconvenience caused.
I worked over solution provided by #Mohamed ِRadwan and updated my socialite route file as follows:
Route::group(['as' => 'auth.'], function () {
Route::group(['middleware' => 'guest'], function () {
// Socialite Routes
Route::any('login/{provider}', [SocialController::class, 'redirect'])->name('social.login');
Route::get('login/{provider}/callback', [SocialController::class, 'callback']);
});
});
This makes the One Tap prompt work as desired with socialite, but the signin in button still doesn't work and throws 419 (means page expired - due to csrf token mismatch) surprising I was sending csrf token as per this post:
How to handle google one tap with laravel csrf
Here is my login button code:
<div id="g_id_onload"
data-client_id="{{ config('services.google.client_id') }}"
data-context="signin"
data-ux_mode="redirect"
data-login_uri="{{ route('frontend.auth.social.login', 'google') }}"
data-_token="{{ csrf_token() }}"
data-auto_prompt="true">
</div>
<div class="g_id_signin"
data-type="standard"
data-shape="rectangular"
data-theme="filled_blue"
data-text="signin_with"
data-size="large"
data-logo_alignment="left">
</div>
It seems that csrf tken is expiring due to the button navigates to google uri to select account and here is where the csrf token gets expired.
So I turned off csrf verification for google socialite route in my VerifyCsrfToken.php middleware as follows:
protected $except = [
'login/google',
];
and voila it worked.
The response from Google one tab is post not get request

Firebase GAS webapp Google popup disappears

I am trying to upgrade my firebase GAS webapp, and previously I had a popup which would log the user in with Google. I'm not sure what I'm doing wrong, but I have upgraded to the new firebase, and am now trying to get the same login working with the new code format.
What is happening, is that the popup comes up and immediately disappears. Can anyone see what I am doing wrong here?
Thanks for you help.
<html lang="en">
<head>
<script src="https://www.gstatic.com/firebasejs/3.1.0/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/3.1.0/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/3.1.0/firebase-database.js"></script>
<script>
var config = {
apiKey: "",
authDomain: "",
databaseURL: "",
storageBucket: ""
};
firebase.initializeApp(config);
</script>
</head>
<body>
<label id="name">First Name</label>
<script>
var provider = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithPopup(provider).then(function(result) {
var user = result.user;
document.getElementById("name").value = user;
}).catch(function(error) {
console.log(error);
});
</script>
</body>
</html>
The problem is solved. I got onto the wonderful people at Firebase, and Dane and I worked through it all till it worked. My code was fine, the issue was in the credentials. If you have the same issue, are all the steps we went through:
Google Configuration
Go to this link google developers console
Go to the credentials option on the left hand side of the screen
Click Browser API Key and copy it
Ensure the API key you copied is the same as the API key in your web app configuration. If it doesn't match, replace the api key in your code with the API key that you copied
Under 'OAuth 2.0 client IDs', click 'Web client (auto created by Google Service)'
Navigate to 'Authorised JavaScript origins' section
Add the full URL you're accessing when you're testing the page, or to be sure, a more general one like https://script.google.com/*
Click on the oAuth web application
For authorized javascript origins please add .firebaseapp.com
For authorized redirect URIs please add .firebaseapp.com/__/auth/handler
Click 'Web client (auto created by Google Service)'
Copy the Client ID and Client Secret
Go to the sign In method page of Firebase Console
Click Google
Click Web SDK configuration
Update the Client ID and Secret with the values you obtained
Click save
Firebase Configuration
Go to this firebase console
Select your project, click on the Auth option on the left hand side of the screen, then select sign in method at the top of the screen
Scroll down to the OAuth redirect domains and see if your custom domain is listed
If not, click Add domain, input your custom domain and click Add
Open Google Chrome and go to the page where your app is deployed.
Right click the page and select 'Inspect'
You will see a Console tab and a down arrow (looks like an inverted triangle). - Click the down arrow and select 'userHtmlFrame (...)'
In the terminal (blue arrow pointing to the right), paste 'window.location.hostname' and click enter
A string will be displayed. Copy it and add it to the authorized domains from your Firebase console (Auth > Sign-in Method> OAuth redirect domains).
I got the same problem , it worked fine while debugging in localhost, but when trying to login from "https://www.example.com" the popup disappear imediately, I fixed it by adding "mydomain.com" in firebase console -> Authentication ->SIGN-IN METHOD tab: scroll down to "OAuth redirect domains"
and click "ADD DOMAIN"
Here is the bare minimum I found needed to get client side auth working in Apps Script. I created a new firebase project. I enabled Google sign-in provider. I added the webapp's origin to the OAuth redirect domain list. I get this by launching the webapp and looking in dev tools inspector. Under the elements tab it will be the src of the top level iframe. It will look something like: n-rn4a4ioahvqauYhvzh2nktp3vxnuap6y7htmacq-0lu-script.googleusercontent.com
code.gs
function doGet() {
return HtmlService.createHtmlOutputFromFile('index')
}
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
</body>
<script src="https://www.gstatic.com/firebasejs/3.2.0/firebase.js"></script>
<script>
// Initialize Firebase
var config = {
apiKey: "AIzaSyADzG0boXmVUJb07K5NbCC0c6SrP8x3Msk",
authDomain: "testclientauth-57c09.firebaseapp.com",
databaseURL: "https://testclientauth-57c09.firebaseio.com",
storageBucket: "testclientauth-57c09.appspot.com",
};
firebase.initializeApp(config);
</script>
<script>
var provider = new firebase.auth.GoogleAuthProvider();
provider.addScope('https://www.googleapis.com/auth/plus.login');
firebase.auth().signInWithPopup(provider).then(function(result) {
var token = result.credential.accessToken;
var user = result.user;
console.log(user)
}).catch(function(error) {
var errorCode = error.code;
var errorMessage = error.message;
var email = error.email;
var credential = error.credential;
});
</script>
</html>
I was just struggling, try to debug around the code and find if there is any wrong in javascript but could find nothing. Then base on answer from Kat, I thought it could not be that complicated, and it might come from some configuration mistakes in firebase website. So go into firebase configuration for your app, in the tab Authentication, there is Sign-in method and there it is, all log-in methods to my app was disabled.
I enabled google authentication and boom, it works as expected. It is also amazed how easy it is for setup authentication system for my app using firebase. Thanks guys from firebase :-)

Preventing automatic sign-in when using Google+ Sign-In

I am in the process of integrating Google+ sign in with my site, which also lets users sign in with Twitter and Facebook. The sign in page of the site therefore has 3 buttons, one for each of the services.
The issue I am having is in the following scenario:
user goes to the sign in page
user signs in successfully with G+
user signs out of my site (but the account is still associated with G+, signing out of the site does not disconnect the G+ account)
user visits the sign in page again
at this stage the Sign in with G+ button is rendered and automatically signs the user into the account associated with G+ without the user having to click the button
The problem is that on revisiting the sign in page, I want the user to have the option of signing in with another service, rather than automatically being signed in with G+. If the user wants to sign in with G+ then they can do so by clicking the button - the user will then be signed in automatically.
Is it possible to prevent this automatic sign in on button render? I can simulate it by using the data-approvalprompt="force" as an attribute on the button, but I don't think this is an ideal solution (the user then has to go through the confirmation process, which I would ideally would like to prevent)
Update
The best supported way to prevent automatic sign-in is to use the API method gapi.auth2.getAuthInstance().signOut() which will prevent automatic sign-in on your site after it has been called. Demo here.
In the demo, the user is signed out when they leave the page as shown in the following code:
window.onbeforeunload = function(e){
gapi.auth2.getAuthInstance().signOut();
};
Now, whenever the user exits the site (e.g. closes the window, navigates away), they will be signed out and the sign in button will not trigger sign-in until the user clicks it.
I don't recommend you do this in your own implementation but instead allow the user to explicitly sign out when they no longer desire want to be signed in. Also, please note that my example is a demo, you probably do not want to sign the user out automatically any time they leave your site.
Original Post
First, you should not be using data-approvalprompt="force" as this will cause extra authorized subtokens to be issued to your application / client and is designed to be used in scenarios where the user needs to be reauthorized after credentials have been lost server-side.
Second, you probably do not want to have the behavior where the user needs to click to sign in because they are already "signed in" to their Google account and it could be confusing to need to sign in (or trigger sign-in) again, separately, for your site.
If you really wanted to do this, you would perform an explicit render for the signin button but would not make the call to gapi.signin.render as documented in the Google+ sign-in documentation until you are aware that the user will not automatically get signed in.
The following code shows how to enable explicit render of the sign-in button:
<script type="text/javascript" src="https://apis.google.com/js/plusone.js">
{"parsetags": "explicit"}
</script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<head>
<script type="text/javascript">
var token = "";
function onSigninCallbackVanilla(authResponse){
// in a typical flow, you show disconnect here and hide the sign-in button
}
The following code shows you how to explicitly render the button:
<span id="signinButton">
<button id = "shim" onclick="gapi.signin.go(); $('#shim').hide();">Show the button</button>
<span
class="g-signin"
data-callback="onSigninCallbackVanilla"
data-clientid="YOUR_CLIENT_ID"
data-cookiepolicy="single_host_origin"
data-requestvisibleactions="http://schemas.google.com/AddActivity"
data-scope="https://www.googleapis.com/auth/plus.login">
</span>
</span>
How you're communicating that the user is signed out of your site is probably going to vary from site to site, but one approach could be to set a cookie indicating the "signed out" state for a user and then using this as the trigger for blocking explicit load. The behavior gets a little trickier when a user visits your site and has disabled cookies or uses a separate, signed-in, browser. To address this, you could do something complicated like querying the user state from your server over XHR on the sign-in callback and pretending not to know the user is signed in to Google+.
Just check for g-auth-window in the callback function:
function google_sign_callback(authResult){
if(authResult['g-oauth-window']){
}else if(authResult['error']) {
}
}
I had this issue and used auth2.disconnect()
function onSignIn(googleUser) {
var profile = googleUser.getBasicProfile();
var auth2 = gapi.auth2.getAuthInstance();
auth2.disconnect();
//do other stuff
}
Edit:
you need to store the token before you disconnect because in some cases id_token will become null after disconnect:
function onSignIn(googleUser) {
var profile = googleUser.getBasicProfile();
var idToken=profile.id_token;
googleUser.disconnect()
//use idToken for server side verification
}
If i'm correct you have your own sign in mechanism for your site and just need google sign in to sign up a user on verified email. in this case you can easily disconnect after you get the profile info.
Next time you load the page you will see "sign in" button instead of "signed in " button.
Unfortunately calling gapi.auth.signOut() made the app to log-in again when I'm requesting user data (neither it is persistent)
So the solution, as suggested by #class is to revoke the token:
$.ajax({
type: 'GET',
url: 'https://accounts.google.com/o/oauth2/revoke?token=' +
gapi.auth.getToken().access_token,
async: false,
contentType: 'application/json',
dataType: 'jsonp',
success: function(result) {
console.log('revoke response: ' + result);
$('#authOps').hide();
$('#profile').empty();
$('#visiblePeople').empty();
$('#authResult').empty();
$('#gConnect').show();
},
error: function(e) {
console.log(e);
}
});
I too has same issue this how i fixed it.I may not sure this is a stander way to do it but still it works fine with me...
add this Google JS from google developer
<script src="https://apis.google.com/js/platform.js" async defer></script>
<script>
function onSuccessG(googleUser) {
var profile = googleUser.getBasicProfile();
console.log('ID: ' + profile.getId()); // Do not send to your backend! Use an ID token instead.
console.log('Name: ' + profile.getName());
console.log('Image URL: ' + profile.getImageUrl());
console.log('Email: ' + profile.getEmail());
}
function onFailureG(error) {
console.log(error);
}
function renderGmail() {
gapi.signin2.render('my-signin2', {
'scope': 'https://www.googleapis.com/auth/plus.login',
'width': 0,
'height': 0,
'longtitle': true,
'theme': 'dark',
'onsuccess': onSuccessG,
'onfailure': onFailureG
});
}
Now add html link and onClick call this renderGmail() function.
SignUp with Gmail
I hope this works...
I am using https://developers.google.com/identity/sign-in/web/build-button to build the sign in button for my web app which gives the user a choice to log in through either Facebook or Google.
This code is pretty easy for obtaining the Id_token.
However it also came with automatic signing in of the user if the user is already signed in.
Thus, adding the following snippet in the beginning of the script helped me control the signup procedure.
window.onbeforeunload = function(e){
gapi.auth2.getAuthInstance().signOut();
};
Thanks!
Our AngularJS solution was:
$scope.$on('event:google-plus-signin-success', function (event, authResult) {
if( authResult.status.method !== "AUTO"){
onGoogleLogIn(authResult[settings.configKeys.googleResponseToken]);
}
});
I have been struggling with this for a while and could not find a way to prevent automatic sign in to Google using the "easy implementation" of the Sign-in
I ended up using the custom integration which does not attempt to auto sign in (also allowed me to change the appearance in the same time)
The accepted answer no longer works when you start to use both Google Sign In and OAuth access tokens for other Google services. The access tokens expire immediately when the user is signed out. Instead, I would recommend the answer from this SO post, which involves attaching a click event handler to the Google sign in button. Only once the user clicks the sign in button and successfully logs into their Google account will the callbacks events fire.
I solved this by adding a click handler to my Google sign-in button. The click handler sets a global Javascript variable google_sign_in to true. When the onSuccess() handler fires (whether automatically on page load, or manually when the user clicks the sign-in button), it first checks whether google_sign_in == true and only then does it continue signing the user in:
<div id="google-signin2" onclick="return do_click_google_signin();"></div>
<script>
var google_sign_in = false; // assume
function do_click_google_signin() {
google_sign_in = true;
}
function onSuccess( googleUser ) {
if ( google_sign_in ) {
// Process sign-in
}
}
// Other redundant login stuff not shown...
</script>

Redirect Express js piped (req.pipe) res properly

Probably I'm doing something wrong but:
In my browser I have a code
window.location = '/some/url'
or element with href="/some url"
in node (express.js) I have following route handler:
app.get('/some/url', function(req, res){
res.redirect('http://www.google.com')
});
Content of the page is loaded with strange flaw Google page. And address bar is not changed (http://localhost:3000/some/url).
I'm making request via intermediate server using
req.pipe(request(host + req.url)).pipe(res) and it seems that piped res does not redirect properly. Any ideas how to solve it?
I needed to use {folllowRedirect: false} option for reqest.js