PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0
Showing posts with label google-oauth. Show all posts
Showing posts with label google-oauth. Show all posts

Monday, October 17, 2022

[FIXED] How to revoke Identity API Token ( Chrome Extension )

 October 17, 2022     api, google-chrome-extension, google-oauth, javascript, oauth     No comments   

Issue

I would like to add a Sign In with Google and a Sign Out button to my Chrome extension.

One technique that uses chrome.identity.getAuthToken for Sign In is described in this tutorial. It works great! When the button is clicked, it shows a popup for authentication and authorization.

But how should I implement the Sign Out button?

I tried to use the removeCachedAuthToken method in the on-click handler of my Sign Out button. With this, the sign-in functionality doesn't work as expected. After, when I pressed the Sign In button again, I got a new token directly without a popup asking the user to authenticate and authorize my extension. I would like users to be able to change their account by signing out. With this technique, that's not possible. How should I implement the sign out functionality to allow for this?


Solution

This has been bugging me too, until I realized that I got mixed up by the difference between sign-in and authorization, sign-out and revoking access.

First, let's not get caught up in the name of the button. Yo may call it Sign Out, but what you actually want to achieve is to let users revoke access for their Google Account, and then log in and grant access to a different account.

If you use removeCacheAuthToken, then authorize again, and see no popup, then that means the extension still has access to certain APIs. To check which apps have been granted access to which Google services, go to permission settings and have a look.

There are several ways to revoke access:

  1. Go to chrome://identity-internals/ and remove the tokens that you want. Then click on the Authorize button, and you should see a popup to choose the Google accounts to grant access.

Of course, that method is for testing only. Your end users won't see the access token for your extension if they visit that page.

  1. When the user clicks on the Revoke access button, or whatever name you call, display a popup that tells them to go to the permission settings page to manually revoke access.

  2. Create a form on the current web page, add access token to the form and submits to the https://oauth2.googleapis.com/revoke endpoint.

From my experience, method 3 seems like an ideal solution, but it was a hit and mix for me. Sometimes I would get an invalid or expired token error, and troubleshooting it is not worth it. I would stick with method for peace of mind.



Answered By - bytrangle
Answer Checked By - Candace Johnson (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] How to auto refresh Google calendar token in python

 October 17, 2022     google-calendar-api, google-oauth, oauth, python     No comments   

Issue

I'm using the following script in order to get all today's events from a specific google calendar. the code works for me. The only thing is that the token expires after ~60 min.

How can I make the token refresh automatically?

import requests
import json
import datetime

API_KEY = "******************"

today = datetime.datetime.now().isoformat()
today_date = str(today[:10])


url = f'https://www.googleapis.com/calendar/v3/calendars/****Calendar_ID****/events?maxResults=99999&access_type=offline&approval_prompt=force&key={API_KEY}'


headers = {
  'Authorization': 'Bearer *********************************************************'
}

params= {
    "timeMin":f"{today_date}T00:00:00-06:00",
    "timeMax":f"{today_date}T23:59:59-06:00",
    "access_type" : "offline",
    "approval_prompt" : "force"
}

response = requests.request("GET", url, headers=headers, params=params)

print(response.text)

Solution

The reason it only works for an hour is that your access token is expiring.

If you follow the official python sample you will notice that it stores the user credentials in a file. It then loads the refresh token when ever it needs to request a new access token

if os.path.exists('token.json'):
    creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
    if creds and creds.expired and creds.refresh_token:
        creds.refresh(Request())
    else:
        flow = InstalledAppFlow.from_client_secrets_file(
            'credentials.json', SCOPES)
        creds = flow.run_local_server(port=0)
    # Save the credentials for the next run
    with open('token.json', 'w') as token:
        token.write(creds.to_json())

In your case you are hard coding the access token, so its hard to tell how you are creating that in the first place. What ever system creates that for you will need to request offline access and get a refresh token back. Once you have that refresh token you can use it to request a new access token.

Code:

POST https://accounts.google.com/o/oauth2/token
client_id={ClientId}.apps.googleusercontent.com&client_secret={ClientSecret}&refresh_token=1/ffYmfI0sjR54Ft9oupubLzrJhD1hZS5tWQcyAvNECCA&grant_type=refresh_token

Response

{
"access_token" : "ya29.1.AADtN_XK16As2ZHlScqOxGtntIlevNcasMSPwGiE3pe5ANZfrmJTcsI3ZtAjv4sDrPDRnQ",
"token_type" : "Bearer",
"expires_in" : 3600
}


Answered By - DaImTo
Answer Checked By - Robin (PHPFixing Admin)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] How to get refresh token for "Sign In With Google"

 October 17, 2022     express, google-oauth, oauth, reactjs, sign-in-with-google     No comments   

Issue

I have been trying to create a refresh token since the access token keep on expiring in 1hr.

 window.onload = function () {
    google.accounts.id.initialize({
      client_id: ,
      callback: handleCredentialResponse,
    });

    google.accounts.id.renderButton(
      document.getElementById("google-signin-button"),
      { theme: "outline", size: "large", shape: "circle" } // customization attributes
    );
    google.accounts.id.prompt(); // also display the One Tap dialog
  };

In this doc from google nothing is mentioned about create a refresh token. https://developers.google.com/identity/gsi/web/guides/overview

Anyone help me out thanks.


Solution

Sign-in or authentication is login and returns and id token and an access token, and identifies the user behind the machine.

Oauth2 is authorization and returns an access token and refresh token granting your application access to a users data.

Signin will not return a refresh token.

If you read further on the page you linked you will fined a section intitled Separated Authentication and Authorization Moments

To obtain an access token for use with Google APIs, or to load some user data, you need to call the Google Identity Services authorization API instead. It's a separated JavaScript API, but packaged together with the authentication API.

Solution to your issue is to use Google Identity Services authorization API instead of signin to authorize a user using Oauth2.



Answered By - DaImTo
Answer Checked By - Cary Denson (PHPFixing Admin)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Friday, October 14, 2022

[FIXED] How to Login with Google?

 October 14, 2022     axios, google-oauth, javascript, node.js, oauth-2.0     No comments   

Issue

I am trying to implement a google oauth 2.0 login without using any libraries in my Node.js application.

I have created an app on the Google API console with the redirect url as http://localhost:3000. During login my response_type is code which returns a one-time use code that needs to be exchanged with the token_endpoint as described here. The exchange is done on my node.js server with the following snippet.

axios({
    url: 'https://www.googleapis.com/oauth2/v4/token',
    method: 'post',
    data: {
      code: code,
      client_id: sso.clientId,
      client_secret: sso.clientSecret,
      redirect_uri: sso.redirect_uri,
      grant_type: 'authorization_code',
    }
  })
  .then((response) => {
    console.log(response.data);
  })
  .catch(function(err) {
    console.log(err.response.data);
  });
But this is is sending me back an error response of

{
  "error": "unsupported_grant_type",
  "error_description": "Invalid grant_type: "
}

instead of the user token.

Please help me identify the issue.

I tried doing a POSTMAN query as well with the same payload in the raw with content-type set to application/json, and it gave me the same error.


Solution

You need to use params in place of your data while making your exchange call through axios, revised block will be like

params: {
    code: code,
    client_id: sso.clientId,
    client_secret: sso.clientSecret,
    redirect_uri: sso.redirect_uri,
    grant_type: 'authorization_code',
}

Hope this helps!



Answered By - David R
Answer Checked By - Katrina (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Saturday, August 6, 2022

[FIXED] How to restrict access with google oauth to one specific domain

 August 06, 2022     flask, google-oauth, oauth, python     No comments   

Issue

So I am trying to create restricted access to one specific domain, because I want to use this application privately.

I browsed on the internet and tried some solutions, but so far I just can't get them to work.

My code looks as follow:

from flask import Flask, redirect, url_for, session, request, jsonify,render_template
from flask_oauthlib.client import OAuth
from bs4 import BeautifulSoup
app = Flask(__name__)

app.config['GOOGLE_ID'] = ""
app.config['GOOGLE_SECRET'] = ""
app.debug = True
app.secret_key = 'development'
oauth = OAuth(app)

google = oauth.remote_app(
    'google',
    consumer_key=app.config.get('GOOGLE_ID'),
    consumer_secret=app.config.get('GOOGLE_SECRET'),
    request_token_params={
        'scope': 'email',
        'hd': 'exampledomain.com'
    },
    base_url='https://www.googleapis.com/oauth2/v1/',
    request_token_url=None,
    access_token_method='POST',
    access_token_url='https://accounts.google.com/o/oauth2/token',
    authorize_url='https://accounts.google.com/o/oauth2/auth',
)

@app.route('/')
def index():
    return redirect(url_for('login'))

@app.route('/login')
def login():
    return google.authorize(callback=url_for('authorized', _external=True))


@app.route('/logout')
def logout():
    session.pop('google_token', None)
    return redirect(url_for('index'))


@app.route('/login/authorized')
def authorized():
    resp = google.authorized_response()
    if resp is None:
        return 'Access denied: reason=%s error=%s' % (
            request.args['error_reason'],
            request.args['error_description']
        )
    session['google_token'] = (resp['access_token'], '')
    return render_template('index.html')


@google.tokengetter
def get_google_oauth_token():
    return session.get('google_token')

I tried it with hd, but I can still manage to login with the other domains. So how can I manage to restrict the access to one domain?


Solution

Had to search for a while, but found my solution on the following github: https://github.com/vistarmedia/google_oauth_flask

The code over there actually was the solution and it worked flawless.

I recommend to use Flask-Dance instead of the solution above, more information can be found over here: https://flask-dance.readthedocs.io/en/latest/



Answered By - Solaiman
Answer Checked By - Timothy Miller (PHPFixing Admin)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] What exactly is the client secret for Google OAuth2?

 August 06, 2022     android, google-developers-console, google-oauth, google-signin, oauth     No comments   

Issue

In Google's OIDC guide, section Exchange code for access token and ID token states that I must provide a client_secret.

If I select the relevant client ID from the API console, I do indeed get a JSON file named client_secret_.... However, none of the fields of the JSON object are named secret or anything similar. (The fields are named client_id, project_id, auth_uri, token_uri, auth_provider_x509_cert_url, and redirect_uris.)

So, where do I get this client_secret?


Solution

I think the secrete used to be in the file but alternatively you can also find it by looking at the page were you downloaded your json file and you can also click the button to reset said secret.

enter image description here

I would make sure that the credentials are looking at are under OAuth 2.0 client IDs and not Service account keys or API keys only the first one needs a secret I believe.

Update from comments: Creating Oauth Client Id for android will not give you a secret because its not needed in android application should should probably be following Add google sign-in to your android App



Answered By - DaImTo
Answer Checked By - Terry (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] How can I authenticate Gmail API without Google Dll

 August 06, 2022     authentication, gmail, gmail-api, google-oauth, oauth     No comments   

Issue

I am trying to use Gmail API without Google Dll, and I would like to use it only with HTTP requests. How can I authenticate and authorize with scopes (like creating a service with Google dll)?

enter image description here

I am getting an error

enter image description here


Solution

You can authenticate to Google using any language that can handle a HTTP POST and a HTTP GET.

Note: client_id, redirect_uri, client_secret are all values that you have set up for your app in Google Developers Console. Scope will depend upon which Google Api you would like to access, more then one can be separated by a comma. I will be using the scope for Google Analytics in this example.

Step one request access:

This is the URL you will need to display to the user requesting access. It is a HTTP Get call and can be placed in any web browser. Note: response_type=code

https://accounts.google.com/o/oauth2/auth?client_id={clientid}&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=https://www.googleapis.com/auth/analytics.readonly&response_type=code

Step Two:

Once they click on the above link you should get a authentication code.

The following request will exchange the code for an access token and a refresh token. This is a HTTP POST Note: grant_type=authorization_code

https://accounts.google.com/o/oauth2/token 
code=4/X9lG6uWd8-MMJPElWggHZRzyFKtp.QubAT_P-GEwePvB8fYmgkJzntDnaiAI&client_id={ClientId&client_secret={ClientSecret}&redirect_uri=urn:ietf:wg:oauth:2.0:oob&grant_type=authorization_code

response:

{ "access_token" : "ya29.1.AADtN_VSBMC2Ga2lhxsTKjVQ_ROco8VbD6h01aj4PcKHLm6qvHbNtn-_BIzXMw", "token_type" : "Bearer", "expires_in" : 3600, "refresh_token" : "1/J-3zPA8XR1o_cXebV9sDKn_f5MTqaFhKFxH-3PUPiJ4" }

Using Refresh token:

The access_token you get from the above request is what you will be using to make requests to the service. After one hour your access token will have expired you will need to request a new access_token you take the refresh_token that you got above and HTTP Post it to: Note: grant_type=refresh_token

https://accounts.google.com/o/oauth2/token 
client_id={ClientId}.apps.googleusercontent.com&client_secret={ClientSecret}&refresh_token=1/ffYmfI0sjR54Ft9oupubLzrJhD1hZS5tWQcyAvNECCA&grant_type=refresh_token 

This is the response:

{ "access_token" : "ya29.1.AADtN_XK16As2ZHlScqOxGtntIlevNcasMSPwGiE3pe5ANZfrmJTcsI3ZtAjv4sDrPDRnQ", "token_type" : "Bearer", "expires_in" : 3600 }

My Full tutorial Google 3 legged oauth2 flow

Usage:

Any request to the Gmail api that you wish to use just tack access_token=yourtoken on the end and you should have access.

or you can set the header.

Authorization  Bearer accessToken


Answered By - DaImTo
Answer Checked By - Mary Flores (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] What does the Google Client Secret in an OAuth2 application give access to?

 August 06, 2022     google-oauth, oauth, oauth-2.0, security     No comments   

Issue

I am implementing a login flow which uses the Google Client ID and Google Client Secret. I am considering the security implications of the Google Client Secret and who should be able to have access to it.

Currently the Client secret is stored in an environment variable, but I would like to know what someone with access to this secret could do with it to determine which developers should have access to this environment variable and if I should setup a different OAuth2 application in development vs production.


Solution

Client id and client secret are similar to a login and password. They give your application the ability to request consent of a user to access their data. If you are storing refresh tokens it would also give the user access to create access tokens from your refresh tokens.

Googles TOS states

Asking developers to make reasonable efforts to keep their private keys private and not embed them in open source projects.

You should not be sharing this with anyone. It should only be used by you and your developers.

Yes Ideally you should have a test and production client ids. Test client id can be used by your developers the only one who should be using your production verified project client ids is your production environment. I would store them in some for for secrete store personally.



Answered By - DaImTo
Answer Checked By - Gilberto Lyons (PHPFixing Admin)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Friday, August 5, 2022

[FIXED] How to check if Google Client_ID and Client_Secret Valid or not

 August 05, 2022     google-api, google-developers-console, google-oauth, node.js, oauth     No comments   

Issue

I am making a module in Node.js where the user can make their own Livestream Bot for youtube. The module requires the user to provide their client id and client secret of their application. But how do I check if the client id and client secret they entered is valid and if it isn't throw an appropriate error. Is there an endpoint to do that?


Solution

There is no way of validating the client id or the client secret. The only way is to try them and if they dont work then display an error to the user.

You could maybe come up with a regex test for the client_id but i wouldn't recommend it as the format has changed several times over the years which would cause problems in your application.

I would just say try to use it if it fails give them an error message.

Note: I hope you are not prompting people to give you their client id and client secret as its against googles TOS for people to shire their client credentials if you are enc urging people to give you their credentials you are telling them to break the TOS.



Answered By - DaImTo
Answer Checked By - Senaida (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] What the attacker could do if he obtains application's client_secret?

 August 05, 2022     google-oauth, oauth, oauth-2.0     No comments   

Issue

I've searched a lot online but with no use. I want to know what could the attacker do if he obtains the client_id and client_secret of an Google's Oauth2 app. Like what the information he would be able to see? Can he edit the app configurations? Can he see other people information?

I haven't worked with Oauth2.0 before so please make your answer simple

Thanks!


Solution

I want to know what could the attacker do if he obtains the client_id and client_secret of an Google's Oauth2 app.

The OAuth 2 Client Secret must be protected. However, if it is leaked the attacker needs one more item. A valid redirect_uri. If the attacker has both along with the (public) Client ID, they might be able to generate OAuth tokens for your account.

The redirect_uri is often valid for http://localhost because developers forget to remove this URI after development completes. This means that someone could run a local server and generate OAuth tokens. This is a big security hole.

What can they do with the OAuth tokens? depends ...

Like what the information he would be able to see? Can he edit the app configurations? Can he see other people information?

You did not specify whose OAuth system, what it is authorizing, etc. Therefore the answer is "it depends".

For Google Cloud, the hacker will need the credentials for someone authorized in Google Cloud. Some systems have very poor security, so as they say, anything can happen and often does with poorly designed security.

In a properly designed system, there are several layers that the hacker needs to get thru. Having the Client Secret helps a lot, but is not a total security failure. The hacker can only authenticate with the system. The next layer, which is authorization, needs to be breached. In a correctly designed system, the hacker will need to authenticate with a user with authorized permissions. If the hacker has that, then you are in big trouble. He might have the keys to do anything he wants. Again, it depends.



Answered By - John Hanley
Answer Checked By - Mary Flores (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] What to do with oauth jwt token inside your backend?

 August 05, 2022     architecture, authentication, google-oauth, oauth, oauth-2.0     No comments   

Issue

Let's imagine we have some website which have 5 different login type:

  • Google
  • Facebook
  • Github
  • Twitter
  • Basic Email + Password

Website is SPA with JWT authentication. But how process the tokens from different Oauth providers to authorize YOUR app backend? Do you convert it to your app jwt? Or you create your backend jwt and send both you and google jwt's at a same time? What is typical design for this kind of stuff?


Solution

Your app should have one authority. But this authority is configured to "trust" other identity providers. This is known as federation. Explanation from IdentityServer, another from auth0.



Answered By - saad
Answer Checked By - Timothy Miller (PHPFixing Admin)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Sunday, July 31, 2022

[FIXED] How to authorize a curl script to Google Oauth after OAuth out-of-band (oob) flow is deprecated?

 July 31, 2022     curl, google-api, google-oauth, oauth     No comments   

Issue

I have a curl script GoogleAuthenticationCurl.sh which i have been using for around ten years to request information from Googles different Google APIs.

This script users installed application credentials to build the consent screen for Googles oauth server. I copy the link and it shows the consent screen.

# Authorization link.  Place this in a browser and copy the code that is returned after you accept the scopes.
https://accounts.google.com/o/oauth2/auth?client_id=[Application Client Id]&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=[Scopes]&response_type=code

Google recently made a change which deprecated the redirect uri of urn:ietf:wg:oauth:2.0:oob. (#instructions-oob)

If I use the link i used to use i get the following

enter image description here

Google wants us to use redirect_uri=http://127.0.0.1:port or http://[::1]:port">http://[::1]:port instead of urn:ietf:wg:oauth:2.0:oob.

So I changed my link to the following and placed it in a web browser

https://accounts.google.com/o/oauth2/auth?client_id=[ClientId]&redirect_uri=http://127.0.0.1b&scope=profile&response_type=code

All went well in the beginning I was able to see the consent screen again and consent to authorization. But instead of getting a authorization code returned I got

enter image description here

This being due to the fact that I am not running a webpage I am just trying to authorize a curl script.

Is there anyway to get my curl script to respond to this request or have google completely removed the ability to authorize a curl Script now?


Solution

Deprecation

Google has deprecated the OOB flow, and so the redirect URL urn:ietf:wg:oauth:2.0:oob is removed since Feb 28, 2022. It is an unsafe feature for clients that cannot listen on an HTTP port.

Migration

You need to migrate to another flow. This does not necessarily mean you cannot use curl. But somehow, you need to be able to receive the redirect call with the necessary code.

Possible fix

  • Use the redirect URL http://127.0.0.1, notice the removed b at the end.
  • After the consent screen, check the URL, you probably find the code there http://127.0.0.1/?code=COPY_THIS_CODE.
  • Run the curl call to request the authorization codes.

Postscript

Postman could be interesting.



Answered By - Ron van der Heijden
Answer Checked By - Marilyn (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] How to request a user to authorize an app to access a Google Drive file using the API

 July 31, 2022     google-api, google-drive-api, google-oauth, oauth     No comments   

Issue

I have a web application and a Google Docs add-on that work together. The Docs add-on passes the web application the Google Drive file id of the Doc it's currently running in.

My web application needs to be able to be able to access/download the file content. I want the web application to use the drive.file scope, so that would require the user to authorize the web application access to the file in Google Drive.

I haven't found any APIs which let me request the user authorize the web app permission to a known file id. I could use the File Picker API, but then the user has to find the file in their Drive (annoying user experience given we already know the file and prone to errors).

Is there any way we can just redirect the user to a Google Drive URL where it asks them to confirm they want to open the file with my web app? Or to preselect the file in the File Picker?


Solution

I don't think you completely understand how scopes work.

The is no way to request permission for just a single file id. Scopes are all or nothing. If you have read only access then you have read only access to all of the files on the users drive account. Same for write access.

Now lets look at Scopes#Drive

  • https://www.googleapis.com/auth/drive.file See, edit, create, and delete only the specific Google Drive files you use with this app

If you request authorization of the user using the drive.file scope. It will allow your application to create files on the users drive account, and access those files which your application created and only those files.

So there is no way to get access to only a single file on a users drive account. If you want access to a file on the users account that wasn't created by your app then your going to have to use drive.readonly or drive. Depending upon if you want read or write access.



Answered By - DaImTo
Answer Checked By - Marie Seifert (PHPFixing Admin)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Wednesday, July 13, 2022

[FIXED] How to authenticate to google in background?

 July 13, 2022     angular, authentication, google-api-js-client, google-oauth, web-deployment     No comments   

Issue

I have build a google authentication as shown in this blog.

https://jorgecf.github.io/2020/04/18/google-oauth-angular

The problem is that it always shows an pop up so that users can login. But I want that login to happen in background where I will be passing the username and password.

Are there a solution for this problem?


Solution

Probably not, due to security reason Google Authentication does not allow pass username and password silently. It works with the help of Authentication Token, that you can store and get rid of username and password prompt if it asked every time, and once it expired you need to authenticate again.



Answered By - Abhinav Kumar
Answer Checked By - Senaida (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Friday, June 24, 2022

[FIXED] How do I have HWI OAuth Bundle behave well in a containerized application behind a reverse proxy?

 June 24, 2022     docker, google-oauth, hwioauthbundle, reverse-proxy     No comments   

Issue

Context

I've been running an intranet admin panel in Symfony 3.x for several years. The users login with google oauth and the system checks if the email matches a validated one in a lookup-list. The oauth client handling is done with the "HWI OAuth Bundle".

In order to start a clean way to migrate this admin panel into SF4 and later to SF5 we've started breaking our monolyth into microservices running in docker.

Moving to docker behind a reverse proxy

Today we were moving this admin panel into a docker. Then we are having the public apache2 doing a ProxyPass towards the docker running the admin panel. Let's imagine the docker runs in http://1.2.3.4:7540 Let's assume the public address is https://admin-europe.example.com

What happens is that the symfony application has a relative URL, as the route google_login configured in the routing.yml and in the service configuration defined in the security.yml:

routing:

# Required by the HWI OAuth Bundle.
hwi_oauth_redirect:
    resource: "@HWIOAuthBundle/Resources/config/routing/redirect.xml"
    prefix:   /connect

hwi_oauth_connect:
    resource: "@HWIOAuthBundle/Resources/config/routing/connect.xml"
    prefix:   /connect

hwi_oauth_login:
    resource: "@HWIOAuthBundle/Resources/config/routing/login.xml"
    prefix:   /login

# HWI OAuth Bundle route needed for each resource provider.
google_login:
    path: /login/check-google

logout:
    path: /logout

security:

firewalls:
    # disables authentication for assets and the profiler, adapt it according to your needs
    dev:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false

    secured_area:
        anonymous: true
        logout:
            path:   /logout
            target: /
            handlers: [ admin.security.logout.handler ]
        oauth:
            resource_owners:
                google:        "/login/check-google"
            login_path:        /
            use_forward:       false
            failure_path:      /

            oauth_user_provider:
                service: admin.user.provider

So when the application was not dockerized, it run properly because the route requested to be the "redirect route" to google was https://admin-europe.example.com/login/check-google.

Nevertheless, now that it's inside the docker when the HWI bundle is building the data block to send to google it requests for this http://1.2.3.4:7540/login/check-google to be authorised as the "redirect URI" but of course it should not. Of course the redirect URI should continue to be https://admin-europe.example.com/login/check-google.

I naturally get this error message:

Google error message

The reverse proxy

We already have in the reverse proxy the ProxyPassReverse and, in fact, the very same configuration has been working hassle-free for over a month with another microservice we already successfully moved (but that service did not need auth, was a public site).

This is natural, as ProxyPassReverse will tackle into http data but the google-oauth info-block is not handled by the ProxyPassReverse, as it's natural.

The problem

The problem here is not to have this address validated (put a domain alias into the private IP address, etc.)

The problem here is how to generate the "proper public URL" from inside the docker without creating a hard-dependency for the container contents in function of the environment it's going to run. Doing so would be an anti-pattern.

Exploring solutions

Of course the "easy" solution would be to "hardcode" the "external route" inside the container.

But this has a flaw. If I also want the same docker to be accessed from, say, https://admin-asia.example.com/ (note the -asia instead of the -europe), I'll run into problems as the asia users will be redirected to the europe route. This is a mere example, don't care about the specific europe-asia thing... the point is that the container should not be conscious of the sorrounding architecture. Or at least, conscious to "interact" but definitively not to have "hardcoded" inside the container things that depend on the environment.

Ie: Forget about the -europe and -asia thing. Imagine the access is admin-1111. It does not make sense that I have to "recompile" and "redeploy" the container if one day I want it to be accessible as admin-2222.

Temporal solution

I think it would solve the problem to point both the route in the rounting.yml and the config in the security.yml to a "parameter" (in 3.x in parameters.yml) and then move that into an Environment Variable when updating to SF4, but I'm unsure on how the cache compiler of the symfony would behave with a route that does not have a value, but a route that "changes dynamically".

Then pass the value of the redirecion when the container is started. This would solve the problem only partially: All the container would be bound to a redirect route set at the time of start, but it still would not solve the case of the same container instance accessed via different names thus needing multiple redirect routes. Instead when running non-dockerized that works as it just takes the "hostname" to build the absolute path on a relative-path definition.

Investigation so far

When accessing, the browser shows I'm going to

https://accounts.google.com/o/oauth2/auth
?response_type=code
&client_id=111111111111-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.apps.googleusercontent.com
&scope=email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fplus.profile.emails.read+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fplus.login
&redirect_uri=http%3A%2F%2Fmy.nice.domain.example.com%3A7040%2Fapp_dev.php%2Flogin%2Fcheck-google

Here we see that the redirect_uri parameter is the place where we'll return after passing the control to google momentarily.

So somebody needs to be building this URL.

I seeked for "redirect_uri" within the source code and I found that the involved classes are GoogleResourceOwner which extends GenericOAuth2ResourceOwner.

Both classes seem to belong to the domain as per the tests passing the $redirectUri as a string which needs to be already built by the caller.

The involved method is public function getAuthorizationUrl($redirectUri, array $extraParameters = array()). This receives the redirect URI and builds the auth URI with the redurect URI encoded as a parameter.

So, who are the consumers/clients of getAuthorizationUrl()?

I only found one single client usage in OAuthUtils, in a line that says return $resourceOwner->getAuthorizationUrl($redirectUrl, $extraParameters); within the function public function getAuthorizationUrl(Request $request, $name, $redirectUrl = null, array $extraParameters = array())

I see that mainly this OAuthUtils is acting as an adapter between the Symfony Request and the OAuth domain model. Within this method we mainly find code to create the $redirectUri.

The cleanest solution for me would be to create a child class OAuthUtilsBehindProxy inheriting from OAuthUtils, overwriting the method getAuthorizationUrl() and having it interpret the X-FORWARDED-* headers of the request, and then have the dependency injection to autowire my class everywhere the OAuthUtils is used with the hope that noone is doing a new OAuthUtils and every user of this class is getting it passed on the constructor.

This would be clean and woul work.

But frankly it seems an overkill to me. I'm pretty sure someone before me has put an app that needs Google OAuth made with HWI behind a reverse proxy and I wonder if there's a "config option" that I'm missing or really I have to re-code all this and inject it via D.I.

So, question

How do I have HWI-OAuth bundle to behave properly when running in a docker container behind a reverse proxy in regards on how to build the "redirect route" for the google-oauth service?

Is there any way to tell either the HWI bundle or either symfony to add a "full-host" prefix IN FUNCTION of the the X-FORWARDED-* headers "if available"? This would leave the docker image "fixed" and would run in "any" environment.


Solution

The underlying reason is the way Symfony generated the full-addresses from a relative path or route name.

Here's the investigation:

  • The method HWI/OAuthUtils::getAuthorizationUrl() is the one that generates the OAUth auth URI and consumes the method Symfony/HttpUtils::generateUri() to get the absolute URI of the redirect_to callback that will be encoded inside the Auth URI.

  • The method Symfony/HttpUtils::generateUri() generates an absolute URI (that in our case will be the callback) and to do so, the method handles 3 general cases:

    • The parameter is already an absolute URI (the return is the parameter without further processing)
    • The parameter is a relative URL (the function calls the Request class to build the proto + host + port + project-path prefix to prepend to the relative URI)
    • The parameter is a route name (the funcion calls the Router class to build the absolute URI)

In my example I was configuring a relative URL (google: "/login/check-google") in the security.yml so HttpUtils was delegating into the Request class.

Looking at the source of the Request class we observe:

  • The Request class is able to use proxy headers to build the absolute class.
  • But for security, by default symfony does not trust that a proxy exists merely because there are X-FORWARDED-* headers in it.
  • Indeed it's more secure plus more flexible.
  • There are 2 levels of security:
    • Somewhere we need to tell the Request class what is the list of trusted IPs that are proxies accessing the application.
    • Somewhere else we need to tell the Request class what specific proxy headers are trusted and what headers are not, even it supports different standards headers (RFC headers, non-RFC apache headers, etc)

Stated here https://symfony.com/blog/fixing-the-trusted-proxies-configuration-for-symfony-3-3 is that you need to configure the trusted proxies in the front-controller by calling the static method Request::setTrustedProxies();

So adding those couple of lines in the front-controller one killing non-nee4ded headers and the other with the IP ranges of the proxies, solved the problem:

# app.php
<?php

use Symfony\Component\HttpFoundation\Request;

$loader = require __DIR__.'/../app/autoload.php';
include_once __DIR__.'/../var/bootstrap.php.cache';

$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();

Request::setTrustedHeaderName( Request::HEADER_FORWARDED, null );   # <-- Kill unneeded header.
Request::setTrustedProxies( [ '192.168.104.0/24', '10.0.0.0/8' ] ); # <-- Trust any proxy that lives in any of those two private nets.

$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);

With this change:

  1. Symfony Request is able to build correct public absolute addresses from relative addresses if called thru a proxy, by deducting the host from HTTP_X_FORWARDED_HOST and HTTP_X_FORWARDED_PORT instead of HTTP_HOST and SERVER_PORT.
  2. Symfony HttpUtils also, as it was delegating to Request.
  3. HWI is in turn able to build a correct absolute callback redirect_to.
  4. HWI can set the proper callback encoded inside the AuthUri.
  5. The AuthURI that contains the proper absolute URI taking in account the proxy effect is sent to google.
  6. Google sees the "public URI" as the one registered in the google configuration.
  7. The workflow completes and the login process can end successfully.


Answered By - Xavi Montero
Answer Checked By - David Goodson (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Wednesday, May 11, 2022

[FIXED] How to specify the scope of Google API to get the birthday

 May 11, 2022     google-api, google-oauth, google-people-api, oauth, symfony     No comments   

Issue

I am trying to get the birthday from the Google API, but the retrieved data in HWIOAuthBundle do not contain it.

I am wondering if the specified scope for google plus api in config.yml is correct or not!

If not, please give a link or the corrected scope.

google:
    type:                google
    client_id:           %client_id%
    client_secret:       %secret_id%
    scope:               "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"
    paths:
        email:           email
        profilepicture:  picture

Solution

I just used and tested it using Try It. I tested it with all of the different scopes.

https://www.googleapis.com/auth/plus.login Know your basic profile info and list of people in your circles.

https://www.googleapis.com/auth/plus.me Know who you are on Google https://www.googleapis.com/auth/userinfo.email View your email address https://www.googleapis.com/auth/userinfo.profile View basic information about your account

It doesn't appear to matter you get back the birthday in all of the scopes. But what does matter is that the Users Birthday must be set to public in the Account. If it's set to anything else, your circles, only you, it's not listed. This appears to be true even when you are trying to see your own information. (Sending Me.)

Update and the year is 2018

The People api now returns the birthday of the current user

People.get However i suspect its linked to google+ so if the user hasn't filled it out you probably wont get info.

GET https://people.googleapis.com/v1/{resourceName=people/*}

Send Resournce name of people/me and birthdays personFields

{
  "resourceName": "people/117200475532672775346",
  "etag": "%EgQBBzcuGgwBAgMEBQYHCAkKCwwiDDQwaGhWYzc3cXJBPQ==",
  "birthdays": [
    {
      "metadata": {
        "primary": true,
        "source": {
          "type": "PROFILE",
          "id": "117200475532672775346"
        }
      },
      "date": {
        "month": 1,
        "day": 6
      }
    },
    {
      "metadata": {
        "source": {
          "type": "ACCOUNT",
          "id": "117200475532672775346"
        }
      },
      "date": {
        "year": 1971,
        "month": 1,
        "day": 6
      }
    }
  ]


Answered By - DaImTo
Answer Checked By - Candace Johnson (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Tuesday, December 28, 2021

[FIXED] How to make friends Facebook Log in code with Google log in code

 December 28, 2021     facebook-php-sdk, google-oauth, social-network-friendship     No comments   

Issue

I have two snippets of code that are each responsible for logging in from their social networks are Facebook and Google.

//GOOGLE
if(isset($_GET['code'])) {

    $token = $google_client->fetchAccessTokenWithAuthCode($_GET["code"]);

    //This condition will check there is any error occur during geting authentication token. If there is no any error occur then it will execute if block of code/
    if (!isset($token['error'])) {
        //Set the access token used for requests
        $google_client->setAccessToken($token['access_token']);

        //Store "access_token" value in $_SESSION variable for future use.
        $_SESSION['access_token'] = $token['access_token'];

        //Create Object of Google Service OAuth 2 class
        $google_service = new Google_Service_Oauth2($google_client);

        //Get user profile data from google
        $data = $google_service->userinfo->get();
        if (!empty($data['sub'])) {
            $_SESSION['user_id'] = $data['sub'];
        }
        if (!empty($data['given_name'])) {
            $_SESSION['user_name'] = $data['given_name'] . " " . $data['family_name'];
        }
        if (!empty($data['email'])) {
            $_SESSION['user_email_address'] = $data['email'];
        }
        if (!empty($data['picture'])) {
            $_SESSION['user_image'] = $data['picture'];
        }
    }
}
//FACEBOOK
$facebook_helper = $facebook->getRedirectLoginHelper();

if(isset($_GET['code'])) {
    if (isset($_SESSION['access_token'])) {
        $access_token = $_SESSION['access_token'];
    } else {
        $access_token = $facebook_helper->getAccessToken();
        $_SESSION['access_token'] = $access_token;
        $facebook->setDefaultAccessToken($_SESSION['access_token']);
    }

    $graph_response = $facebook->get("/me?fields=name,email", $access_token);
    $facebook_user_info = $graph_response->getGraphUser();

    if (!empty($facebook_user_info['id'])) {
        $_SESSION['user_image'] = 'http://graph.facebook.com/' . $facebook_user_info['id'] . '/picture';
    }
    if (!empty($facebook_user_info['id'])) {
        $_SESSION['user_id'] = $facebook_user_info['id'];
    }
    if (!empty($facebook_user_info['name'])) {
        $_SESSION['user_name'] = $facebook_user_info['name'];
    }
    if (!empty($facebook_user_info['email'])) {
        $_SESSION['user_email_address'] = $facebook_user_info['email'];
    }
} else {
    // Get login url
    $facebook_permissions = ['email']; // Optional permissions
    $facebook_login_url = $facebook_helper->getLoginUrl('https://2goe.com/demo/'.$lang.'/home/', $facebook_permissions);
}

When they are together, then:

  1. When you click Google log in, redirectURL responds with server error 500.
  2. And Facebook does not return user data, which is requested in the code.

But if, for example, you delete the code of one of the social networks, it individually works fine. I myself tried to somehow paint 2 codes into one code, but to no avail. I also split them into different files, but this also did not bring any results.

Can you please help me somehow combine them correctly so that there are no such conflicts between them.


Solution

The issue you are having is that you are setting things with the same session names. $_GET['code'] could be facebook or google there is no way for you to know which one it is.

The easiest solution would be to run the google code first. Then alter the if statement for Facebook a little.

If you do something like this the code for facebook will look for a code find a code but it will also look for an error from Google. If google spit back an error then you try the code with facebook. If it did not return an error then you know the code was most likely used by Google.

if(isset($_GET['code'] && isset($token['error'])) {


Answered By - DaImTo
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Older Posts Home
View mobile version

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
All Comments
Atom
All Comments

Copyright © PHPFixing