I am trying to make a program that creates a YouTube playlist in the user’s account. Using YouTube’s client secrets file and OAuth 2.0, I managed to get it to work: it generates a link in the console that provides an authorization code. The code must be copy-pasted into the console to log into the account and view the YouTube playlist in said account. However, I want to bypass this authentication step because (1) it’s not user-friendly, (2) I have no idea how that would even work for a user who doesn’t have access to the console (this is part of a web app run online using Flask).
How can I have the authentication/sign-in link automatically pop up in a new tab, and have the user ONLY need to select a Google account and allow the app’s YouTube permissions WITHOUT inputting any code into the server?
Thank you so much for the help! The link to the relevant portion of code is below.
Repl link: https://replit.com/@popcorns/Google-Sign-In?v=1
1 Like
You can accomplish this by implementing OAuth 2.0 web server applications using Flask.
Your code will need a bit of tinkering as follows:
import os
from flask import Flask, request, redirect, session
from google_auth_oauthlib.flow import Flow
from googleapiclient.discovery import build
from google.auth.transport.requests import Request
app = Flask(__name__)
app.secret_key = 'flask_key_here'
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
# Here we have the scope of the application
SCOPES = ["https://www.googleapis.com/auth/youtube.force-ssl"]
@app.route('/')
def index():
if 'credentials' not in session:
return redirect('authorize')
creds = session['credentials']
youtube = build(
'youtube', 'v3', credentials=creds)
return "You're logged in."
@app.route('/authorize')
def authorize():
flow = Flow.from_client_secrets_file(
'c_new.json',
scopes=SCOPES,
redirect_uri='http://localhost:5000/oauth2callback')
authorization_url, state = flow.authorization_url(
# With this we enable offline access so that you can refresh an access token.
access_type='offline',
# You should enable incremental authorization.
include_granted_scopes='true')
# Store the state so the callback can verify the auth server response.
session['state'] = state
return redirect(authorization_url)
@app.route('/oauth2callback')
def oauth2callback():
state = session['state']
flow = Flow.from_client_secrets_file(
'c_new.json',
scopes=SCOPES,
state=state,
redirect_uri='http://localhost:5000/oauth2callback')
flow.fetch_token(authorization_response=request.url)
session['credentials'] = flow.credentials
return redirect('/')
if __name__ == '__main__':
# If you are running app locally
app.run('localhost', 5000, debug=True)
1 Like
Hi @WindLother,
Thank you so much for the code! It makes a lot of sense with your explanation.
I suppose I should’ve mentioned earlier, but I’m using ngrok to view the Flask app in my web browser. Therefore, the redirect uri changes every time (because ngrok generates a new link every time you run the app) and I’m not sure how to fix this problem after many tries. Localhost does not work on my computer, which is why I have to resort to ngrok.
Sorry to ask this of you again! Looking forward to your response 
Yeah it makes sense you would resort to ngrok.
There is a workaround, when you create a tunnel using ngrok, the tunnel’s URL is displayed in the console. This URL can be programmatically accessed using the ngrok API. You can fetch the public URL and then use that as your redirect_uri
.
So you create a Python function to get the ngrok tunnel URL:
import requests
def fetch_ngrok_tunnel():
ngrok_api_url = "http://localhost:4040/api/tunnels"
response = requests.get(ngrok_api_url)
json_data = response.json()
for tunnel in json_data["tunnels"]:
if tunnel['proto'] == 'https':
tunnel_url = tunnel['public_url']
return tunnel_url
return None
You can then use this function to fetch the ngrok URL and append the path of your callback endpoint ('/oauth2callback'
), like this:
redirect_uri = fetch_ngrok_tunnel() + '/oauth2callback'
This way, the redirect_uri
you provide to the OAuth flow will match the public ngrok URL.
Hi @WindLother,
The code works fine, but when I run it I get an Error 400: redirect_uri_mismatch. It looks like I have to register the URI in Google Cloud Console, but I don’t know how to do that automatically in Python, especially since it’s a constantly-changing ngrok link.
Hopefully for the very last time, would you be able to help me with this? 
You can check this video:
Hi @WindLother,
Unfortunately, the video didn’t show how to input a dynamically updating URL into Google Cloud Console. But I’m trying a workaround. Since the redirect URI works when I use locahost:5000, is there a way to modify the (super helpful) code you suggested so that it redirects from localhost:5000 to the ngrok link?
Thanks~
I tested the code and looks like it worked with the cases I tested buuut can be a different case to you
import requests
from flask import Flask, redirect, request, session, url_for
from google_auth_oauthlib.flow import Flow
from google.auth.transport.requests import Request
app = Flask(__name__)
app.secret_key = 'flask_key_here' #secret key
# Configure the OAuth 2.0 client with your client_secrets.json file
flow = Flow.from_client_secrets_file(
'client_secrets.json', scopes=['https://www.googleapis.com/auth/youtube.force-ssl'],
redirect_uri='http://localhost:5000/callback')
def fetch_ngrok_tunnel():
ngrok_api_url = "http://localhost:4040/api/tunnels"
response = requests.get(ngrok_api_url)
json_data = response.json()
for tunnel in json_data["tunnels"]:
if tunnel['proto'] == 'https':
tunnel_url = tunnel['public_url']
return tunnel_url
return None
@app.route('/')
def index():
authorization_url, state = flow.authorization_url()
# Store the state in user's session for validation in the callback route
session['state'] = state
return redirect(authorization_url)
@app.route('/callback')
def callback():
# Protects against CSRF
if request.args.get('state', '') != session.get('state'):
abort(500)
# Get authorization response from the callback request
flow.fetch_token(authorization_response=request.url)
# Get the ngrok link from the function or a default value
ngrok_link = fetch_ngrok_tunnel() or 'http://localhost:5000'
# Redirect to the ngrok link
return redirect(ngrok_link)
1 Like
Aren’t you using a Repl? (this is the Replit forums after all)
1 Like
Please use secrets for this:
app.secret_key = os.environ["flask_key"] # secret key
3 Likes