gradio-app / gradio

Build and share delightful machine learning apps, all in Python. 🌟 Star to support our work!
http://www.gradio.app
Apache License 2.0
29.84k stars 2.22k forks source link

Google Authentication #2790

Closed yuvalkirstain closed 2 months ago

yuvalkirstain commented 1 year ago

In some apps, we want to filter harmful users or bots. I think that having a component that enables google authentication (rather than a username, passowrd authentication) in gradio can be very helpful.

Describe the solution you'd like
I'd like to have an authentication component that receives the details of the user so it can decide if the user may access the app or not.

yuvalkirstain commented 1 year ago

This code is partial (and unfortunately non-elegant) solution. It:

  1. creates a new app that mounts the gradio app
  2. the new app is in charge of the google authentication.

A key aspect that is missed here, is that in the case that the user goes straight ahead to the gradio endpoint, there is no redirection to login and the user is stuck there. Is there a way by any chance to redirect from within the gradio demo back to \login?

import json
from authlib.integrations.base_client import OAuthError
from fastapi import FastAPI
from starlette.middleware.sessions import SessionMiddleware
from starlette.responses import HTMLResponse, RedirectResponse
from starlette.requests import Request
import gradio as gr
from authlib.integrations.starlette_client import OAuth
from starlette.config import Config

# CODE FOR NEW APP

app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key="!secret")

config = Config('.env')
oauth = OAuth(config)

CONF_URL = 'https://accounts.google.com/.well-known/openid-configuration'
oauth.register(
    name='google',
    server_metadata_url=CONF_URL,
    client_kwargs={
        'scope': 'openid email profile'
    }
)

@app.get('/')
async def homepage(request: Request):
    user = request.session.get('user')
    if user:
        data = json.dumps(user)
        html = (
            f'<pre>{data}</pre>'
            '<a href="/logout">logout</a>'
            '<br>'
            '<a href="/gradio">demo</a>'
        )
        return HTMLResponse(html)
    return HTMLResponse('<a href="/login">login</a>')

@app.get('/login')
async def login(request: Request):
    redirect_uri = request.url_for('auth')
    return await oauth.google.authorize_redirect(request, redirect_uri)

@app.get('/auth')
async def auth(request: Request):
    print(f"before request user {request.session.get('user')}")
    try:
        token = await oauth.google.authorize_access_token(request)
    except OAuthError as error:
        return HTMLResponse(f'<h1>{error.error}</h1>')
    user = token.get('userinfo')
    if user:
        request.session['user'] = dict(user)
    print(f"after request user {request.session.get('user')}")
    return RedirectResponse(url='/')

@app.get('/logout')
async def logout(request: Request):
    request.session.pop('user', None)
    return RedirectResponse(url='/')

# CODE FOR MOUNTED GRADIO APP

def update(name, request: gr.Request):
    return f"Welcome to Gradio, {name}!\n{request.request.session.get('user')}"

def make_demo_visible(request: gr.Request):
    if request.request.session.get('user'):
        return gr.update(visible=True), gr.update(visible=True), gr.update(visible=True), gr.update(visible=False)
    return gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(value="Looks like you are not logged in. Please login at the main app.")

with gr.Blocks() as demo:
    start_btn = gr.Button("Press Here to initialize the demo!")

    with gr.Row():
        inp = gr.Textbox(placeholder="What is your name?", visible=False)
        out = gr.Textbox(visible=False)

    btn = gr.Button("Run", visible=False)

    start_btn.click(make_demo_visible, outputs=[inp, out, btn, start_btn])
    btn.click(fn=update, inputs=inp, outputs=out)

gradio_app = gr.mount_gradio_app(app, demo, "/gradio")
davidbernat commented 1 year ago

Google Firebase offers an excellent solution to this use case. I have a useful API on the backend server data processing that handles inbound authorization requests from a Google Firebase front-end. (I would be happy to offer my code to anyone who asks.) Google Firebase front-end support is excellent. I have a React UI from previous repositories and directing users to that landing page; require them to log in with the Firebase token or via Google Login, then redirects the user to the primary front-end of the app (the Gradio UI in this case). I have not installed that into my current Gradio UI only because I am only now developing the app via Gradio, though the software has served me excellently for several years now. It would even be relatively easy to embed an email and UUID into an existing Gradio demo which communicates first with the backend for Google Firebase; which then, upon returning confirmation of the email-UUID match makes numerous, otherwise invisible and inactive Gradio UI blocks appear. I think that might even be easier in a first pass to do this because there is no need to mount a second service for the front-end. This solution is similar to @yuvalkirstain with Google Firebase handling the auth.

zinoubm commented 1 year ago

Hi @davidbernat, inroder to request permissions from google oauth. my app needs to redirect to a url provided by google api. The problem is that gradio doesn't offer a redirect feature.

zinoubm commented 1 year ago

Hey @yuvalkirstain , Can you give me a way to redirect the user to a given url, I'll be really happy if you can help me.

AGronowski commented 1 year ago

Google Firebase offers an excellent solution to this use case. I have a useful API on the backend server data processing that handles inbound authorization requests from a Google Firebase front-end. (I would be happy to offer my code to anyone who asks.) Google Firebase front-end support is excellent. I have a React UI from previous repositories and directing users to that landing page; require them to log in with the Firebase token or via Google Login, then redirects the user to the primary front-end of the app (the Gradio UI in this case). I have not installed that into my current Gradio UI only because I am only now developing the app via Gradio, though the software has served me excellently for several years now. It would even be relatively easy to embed an email and UUID into an existing Gradio demo which communicates first with the backend for Google Firebase; which then, upon returning confirmation of the email-UUID match makes numerous, otherwise invisible and inactive Gradio UI blocks appear. I think that might even be easier in a first pass to do this because there is no need to mount a second service for the front-end. This solution is similar to @yuvalkirstain with Google Firebase handling the auth.

Hey @davidbernat , I'm trying to implement exactly this, add authentication to a gradio app, pass in a UUID and make blocks visible after comparing the UUID with what's in a database. If you'd be willing to share any code or give any advice, that would be really helpful!

jerpint commented 1 year ago

Has anyone made any progress on this? I would like to also have firebase authenticate users and redirect to my app once authenticated

zinoubm commented 1 year ago

@jerpint I found that gradio is built on top of fastapi, so for me I mounted gradio app on top of a fastapi app that handle authentication.

jerpint commented 1 year ago

@zinoubm i was thinking of doing something similar, do you have a working example?

zinoubm commented 1 year ago

@jerpint Sorry about that, It was for a client so I can't share it. But I believe the docs have some useful resources.

zinoubm commented 1 year ago

@jerpint , You may find this helpful https://gradio.app/sharing-your-app/#mounting-within-another-fastapi-app

davidbernat commented 1 year ago

I solved this problem. Please mark this issue as complete. Starlight LLC. Thanks.

kambleakash0 commented 1 year ago

Solved? Where can I see this enhancement @davidbernat?

I just came across a case where I will need to integrate Google login to a Gradio app and stumbled upon this discussion just now.

davidbernat commented 1 year ago

Unfortunately until Google and Apple shift toward more open models of research and data in their AI division we have decided to close our doors to each company here at Starlight LLC and Starlight.AI LLC. You may feel free to reach out to me directly, though I wish to not discuss this project at this time as Google has shifted its backend priorities anyway, and legal action is already in discussion.

kambleakash0 commented 1 year ago

So there's no other way than to wrap it in a FastAPI app and then implement auth for that FastAPI app?

dhruv-anand-aintech commented 1 year ago

@davidbernat you've not solved anything as far as I can see. Please keep this issue open until the functionality is implemented in Gradio, or there is a simple process we can follow to make it work well.

kambleakash0 commented 1 year ago

@yuvalkirstain Have you hosted this somewhere so it'll be helpful for us to take a look?

AGronowski commented 1 year ago

@zinoubm I know you can't share your code, but could you explain how you access the gr.Request to get the user information? I tried the above solution by @yuvalkirstain but it only works when the queue is disabled. Enabling the queue makes request.request be None and the solution no longer works.

ambiSk commented 11 months ago

Any final update on this thread? How to enable Google SSO for Gradio Apps, I tried the mount_gradio_app function based on @yuvalkirstain suggestion, but still when I directly go to the pathway, it overrides the login and directly make it accessible. Someone provide alternative approach for Google SSO

ambiSk commented 10 months ago

What if we use some Middleware from FastAPI?

davidbernat commented 10 months ago

The issue is not the API. All that is required is an HTML element that can create API calls via JS or, better yet, a hook into a Python function. Unfortunately, my understanding is that Gradio is not providing those yet, and I created my own.

ambiSk commented 10 months ago

@yuvalkirstain @jerpint @kambleakash0 @dhruv-anand-aintech I think I found a solution to enforce authentication on gradio app, using a custom middleware helps with this, here's the code of @yuvalkirstain with the middleware:

import json
from authlib.integrations.base_client import OAuthError
from fastapi import FastAPI
from starlette.middleware.sessions import SessionMiddleware
from starlette.responses import HTMLResponse, RedirectResponse
from starlette.requests import Request
import gradio as gr
from authlib.integrations.starlette_client import OAuth
from starlette.config import Config

# CODE FOR NEW APP

app = FastAPI()

config = Config('.env')
oauth = OAuth(config)

CONF_URL = 'https://accounts.google.com/.well-known/openid-configuration'
oauth.register(
    name='google',
    server_metadata_url=CONF_URL,
    client_kwargs={
        'scope': 'openid email profile'
    }
)

# The Middleware that enforces authentication on /gradio app
@app.middleware("http")
async def check_authentication(request: Request, call_next):
    if request.url.path.startswith('/login') or request.url.path.startswith('/auth'):
        # Skip authentication check for login and authentication routes
        return await call_next(request)

    if request.url.path=='/gradio/api/predict' or request.url.path=='/gradio/reset':
        return await call_next(request)

    user = request.session.get("user")
    if not user:

        # User is not logged in, redirect to login page
        return RedirectResponse(url="/login")

    return await call_next(request)

@app.get('/')
async def homepage(request: Request):
    user = request.session.get('user')
    if user:
        data = json.dumps(user)
        html = (
            f'<pre>{data}</pre>'
            '<a href="/logout">logout</a>'
            '<br>'
            '<a href="/gradio">demo</a>'
        )
        return HTMLResponse(html)
    return HTMLResponse('<a href="/login">login</a>')

@app.get('/login')
async def login(request: Request):
    redirect_uri = request.url_for('auth')
    return await oauth.google.authorize_redirect(request, redirect_uri)

@app.get('/auth')
async def auth(request: Request):
    print(f"before request user {request.session.get('user')}")
    try:
        token = await oauth.google.authorize_access_token(request)
    except OAuthError as error:
        return HTMLResponse(f'<h1>{error.error}</h1>')
    user = token.get('userinfo')
    if user:
        request.session['user'] = dict(user)
    print(f"after request user {request.session.get('user')}")
    return RedirectResponse(url='/')

@app.get('/logout')
async def logout(request: Request):
    request.session.pop('user', None)
    return RedirectResponse(url='/')

# CODE FOR MOUNTED GRADIO APP

def update(name, request: gr.Request):
    return f"Welcome to Gradio, {name}!\n{request.request.session.get('user')}"

def make_demo_visible(request: gr.Request):
    if request.request.session.get('user'):
        return gr.update(visible=True), gr.update(visible=True), gr.update(visible=True), gr.update(visible=False)
    return gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(value="Looks like you are not logged in. Please login at the main app.")

with gr.Blocks() as demo:
    start_btn = gr.Button("Press Here to initialize the demo!")

    with gr.Row():
        inp = gr.Textbox(placeholder="What is your name?", visible=False)
        out = gr.Textbox(visible=False)

    btn = gr.Button("Run", visible=False)

    start_btn.click(make_demo_visible, outputs=[inp, out, btn, start_btn])
    btn.click(fn=update, inputs=inp, outputs=out)

gradio_app = gr.mount_gradio_app(app, demo, "/gradio")
app.add_middleware(SessionMiddleware, secret_key="!secret")

In the middleware you'll find these lines of code:

    if request.url.path=='/gradio/api/predict' or request.url.path=='/gradio/reset':
        return await call_next(request)

I included this because I found that when using this middleware, the fastapi makes POST request to /gradio/api/predict and /gradio/reset which messes up with gradio functionality, instead of making POST request over those, it has to make POST request on /api/predict and /reset to make gradio app function. I think developers need to work on routes.py, so that if someone makes multiple pathways to multiple gradio app they don't need to write condition statement for each app.

Please provide feedback on this solution and let me know if there's a better solution than this

pseudotensor commented 10 months ago

@ambiSk Sorry for naive question, but how do you run the above block. When I run it, nothing stops and it just completes the code without effect.

ambiSk commented 10 months ago

@pseudotensor I understand you are not aware of how to launch a FastAPI app. Install uvicorn in your python environment using:

pip installl -U uvicorn

Then do either of the following:

abidlabs commented 6 months ago

Hey! We've now made it possible for Gradio users to create their own custom components -- meaning that you can write some Python and JavaScript (Svelte), and publish it as a Gradio component. You can use it in your own Gradio apps, or share it so that anyone can use it in their Gradio apps. Here are some examples of custom Gradio components:

You can see the source code for those components by clicking the "Files" icon and then clicking "src". The complete source code for the backend and frontend is visible. In particular, its very fast if you want to build off an existing component. We've put together a Guide: https://www.gradio.app/guides/five-minute-guide, and we're happy to help. Hopefully this will help address this issue.

pseudotensor commented 6 months ago

@ambiSk

I can't get that to work as end-to-end example. So I have this code block as main.py:

import json

import uvicorn
from authlib.integrations.base_client import OAuthError
from fastapi import FastAPI
from starlette.middleware.sessions import SessionMiddleware
from starlette.responses import HTMLResponse, RedirectResponse
from starlette.requests import Request
import gradio as gr
from authlib.integrations.starlette_client import OAuth
from starlette.config import Config

# CODE FOR NEW APP

app = FastAPI()

config = Config('.env')
oauth = OAuth(config)

CONF_URL = 'https://accounts.google.com/.well-known/openid-configuration'
oauth.register(
    name='google',
    server_metadata_url=CONF_URL,
    client_kwargs={
        'scope': 'openid email profile'
    }
)

# The Middleware that enforces authentication on /gradio app
@app.middleware("http")
async def check_authentication(request: Request, call_next):
    if request.url.path.startswith('/login') or request.url.path.startswith('/auth'):
        # Skip authentication check for login and authentication routes
        return await call_next(request)

    if request.url.path=='/gradio/api/predict' or request.url.path=='/gradio/reset':
        return await call_next(request)

    user = request.session.get("user")
    if not user:

        # User is not logged in, redirect to login page
        return RedirectResponse(url="/login")

    return await call_next(request)

@app.get('/')
async def homepage(request: Request):
    user = request.session.get('user')
    if user:
        data = json.dumps(user)
        html = (
            f'<pre>{data}</pre>'
            '<a href="/logout">logout</a>'
            '<br>'
            '<a href="/gradio">demo</a>'
        )
        return HTMLResponse(html)
    return HTMLResponse('<a href="/login">login</a>')

@app.get('/login')
async def login(request: Request):
    redirect_uri = request.url_for('auth')
    return await oauth.google.authorize_redirect(request, redirect_uri)

@app.get('/auth')
async def auth(request: Request):
    print(f"before request user {request.session.get('user')}")
    try:
        token = await oauth.google.authorize_access_token(request)
    except OAuthError as error:
        return HTMLResponse(f'<h1>{error.error}</h1>')
    user = token.get('userinfo')
    if user:
        request.session['user'] = dict(user)
    print(f"after request user {request.session.get('user')}")
    return RedirectResponse(url='/')

@app.get('/logout')
async def logout(request: Request):
    request.session.pop('user', None)
    return RedirectResponse(url='/')

# CODE FOR MOUNTED GRADIO APP

def update(name, request: gr.Request):
    return f"Welcome to Gradio, {name}!\n{request.request.session.get('user')}"

def make_demo_visible(request: gr.Request):
    if request.request.session.get('user'):
        return gr.update(visible=True), gr.update(visible=True), gr.update(visible=True), gr.update(visible=False)
    return gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(value="Looks like you are not logged in. Please login at the main app.")

with gr.Blocks() as demo:
    start_btn = gr.Button("Press Here to initialize the demo!")

    with gr.Row():
        inp = gr.Textbox(placeholder="What is your name?", visible=False)
        out = gr.Textbox(visible=False)

    btn = gr.Button("Run", visible=False)

    start_btn.click(make_demo_visible, outputs=[inp, out, btn, start_btn])
    btn.click(fn=update, inputs=inp, outputs=out)

gradio_app = gr.mount_gradio_app(app, demo, "/gradio")
app.add_middleware(SessionMiddleware, secret_key="!secret")

and I run:

uvicorn main:app 

As soon as I go to http://127.0.0.1:8000 it shows:

Sign in with Google
Access blocked: Authorization Error

XXXX@gmail.com
The OAuth client was not found.
If you are a developer of this app, see [error details](https://accounts.google.com/).
Error 401: invalid_client

I removed my email as XXXX.

Do I need to fill .env file with something about who's authorized? I'm confused.

thiner commented 6 months ago

@pseudotensor Did you register your app in Google authentication service(I am not familiar to Google SSO, this is just a convenient reference to the product, not the product name)? Because the error message seems you passed a wrong client_id to Google, and Google refused to proceed.

pseudotensor commented 6 months ago

Thanks, that helped. Now I'm stuck with my own app hanging after reaching loading (visual loading of gradio app with spinning candy looking thing) and unable to find style css files etc.

delip commented 3 months ago

@abidlabs I am not able to understand how the custom component support will solve the OP's auth issue. I am in a similar situation as the OP. Perhaps it will help if you can post a hello world gradio code that uses firebase google auth and posts a custom message for the user.

abidlabs commented 3 months ago

Sorry for the late response @delip -- will work on something for this

abidlabs commented 3 months ago

For anyone who'd like to test this feature, I have a PR ready here: https://github.com/gradio-app/gradio/pull/7557

You can install this Gradio from this PR by doing:

pip install -q https://gradio-builds.s3.amazonaws.com/abf7bda7be2e2375e09514fde0ea8c86491900af/gradio-4.19.2-py3-none-any.whl

And you can add e.g. Google OAuth to your Gradio app like this:

import os
from authlib.integrations.starlette_client import OAuth, OAuthError
from fastapi import FastAPI, Depends, Request
from starlette.config import Config
from starlette.responses import RedirectResponse
from starlette.middleware.sessions import SessionMiddleware
import uvicorn
import gradio as gr

app = FastAPI()

# OAuth settings
GOOGLE_CLIENT_ID = "..."
GOOGLE_CLIENT_SECRET = "..."
SECRET_KEY = "..."

# Set up OAuth
config_data = {'GOOGLE_CLIENT_ID': GOOGLE_CLIENT_ID, 'GOOGLE_CLIENT_SECRET': GOOGLE_CLIENT_SECRET}
starlette_config = Config(environ=config_data)
oauth = OAuth(starlette_config)
oauth.register(
    name='google',
    server_metadata_url='https://accounts.google.com/.well-known/openid-configuration',
    client_kwargs={'scope': 'openid email profile'},
)

SECRET_KEY = os.environ.get('SECRET_KEY') or "a_very_secret_key"
app.add_middleware(SessionMiddleware, secret_key=SECRET_KEY)

# Dependency to get the current user
def get_user(request: Request):
    user = request.session.get('user')
    if user:
        return user['name']
    return None

@app.get('/')
def public(user: dict = Depends(get_user)):
    if user:
        return RedirectResponse(url='/gradio')
    else:
        return RedirectResponse(url='/login-demo')

@app.route('/logout')
async def logout(request: Request):
    request.session.pop('user', None)
    return RedirectResponse(url='/')

@app.route('/login')
async def login(request: Request):
    redirect_uri = request.url_for('auth')
    return await oauth.google.authorize_redirect(request, redirect_uri)

@app.route('/auth')
async def auth(request: Request):
    try:
        access_token = await oauth.google.authorize_access_token(request)
    except OAuthError:
        return RedirectResponse(url='/')
    request.session['user'] = dict(access_token)["userinfo"]
    return RedirectResponse(url='/')

with gr.Blocks() as login_demo:
    gr.Button("Login", link="/login")

app = gr.mount_gradio_app(app, login_demo, path="/login-demo")

def greet(request: gr.Request):
    return f"Welcome to Gradio, {request.username}"

with gr.Blocks() as main_demo:
    m = gr.Markdown("Welcome to Gradio!")
    gr.Button("Logout", link="/logout")
    main_demo.load(greet, None, m)

app = gr.mount_gradio_app(app, main_demo, path="/gradio", auth_dependency=get_user)

if __name__ == '__main__':
    uvicorn.run(app)
pseudotensor commented 3 months ago

Awesome, I'll try.

pseudotensor commented 3 months ago

Minor thing, your command above has extra pip install !

pseudotensor commented 3 months ago

@abidlabs

The confusion with the mount way is that all the options I'd normally pass to launch are not available. So I don't know how to reproduce all the normal things I'd do.

pseudotensor commented 3 months ago

@abidlabs I tried to use 0.0.0.0 as host in uvicorn so I could access it remotely (default of 127.0.0.1 doesn't allow that). But I get:

image

The exact same setup locally works fine, but only with default of 127.0.0.1, not 0.0.0.0.

I'm unsure of what host to use. E.g. the machine I'd want to use it on has an internal IP that doesn't even match the external IP.

Separate issue is for my home system, it has IP that is dynamic. If I try to use the hostname, then uvicorn itself says it cannot bind on that address.

abidlabs commented 3 months ago

The confusion with the mount way is that all the options I'd normally pass to launch are not available. So I don't know how to reproduce all the normal things I'd do.

Correct, this is running in a larger FastAPI app, so you'd need to configure uvicorn instead of configuring just the Gradio app. If you share more details, I can perhaps suggest the equivalent configurations in uvicorn

@abidlabs I tried to use 0.0.0.0 as host in uvicorn so I could access it remotely (default of 127.0.0.1 doesn't allow that). But I get:

Typically, you'd use Google OAuth when you have a publicly accessible Gradio app, and you would use the public address, not 0.0.0.0. For testing, I was able to get it to work with 127.0.0.1, you should be able to do the same (you'll need to set it one of the valid IP addresses in the Google Developer Console -- you might be able to do the same with 0.0.0.0, I have not tested). Then, when your app is publicly accessible, you would also add that URL / IP address in the Google Developer Console.

I would also try clicking on this link to see if they provide any additional info:

image
pseudotensor commented 3 months ago

The scenario I explained that was public is still issue in that the IP address visible externally is different than internally, even though external one is public and visible. AWS is like this, e.g. 10.10.0.x inside but 52.0.25.y outside.

If I try to use outside IP with uvicorn, it can't bind. When I try to use inside, it binds but auth fails with that message above. Using 127.0.0.1 launches but is not reachable from outside.

The clicked box isn't super interesting, just says:

Error 400: invalid_request
Request details: redirect_uri=http://<external ip>:<real port>/auth

If I try using the nginx proxy name and real port, then it goes to https://gpt.h2o.ai/login-demo/ and just says{"detail":"Not Found"}`.

So there's no combination out of the about 6 ways I've tried that work. I don't see anything in google console oauth client setup that is related to adding IP addressed, just client ID and secret. I'm not sure what to set super secret thing to, so I just made something up, which didn't have issues locally.

Can you try to make it work on AWS where the internal and external IP are different?

abidlabs commented 3 months ago

From what I understand, you should use 127.0.0.1 (or 0.0.0.0) on uvicorn, and then use nginx to forward public traffic to 127.0.0.1:port, and then use the public IP address in the Google Console but let me look into this to confirm

abidlabs commented 3 months ago

I put together an example that works on HuggingFace Spaces: https://huggingface.co/spaces/gradio/oauth-example (all the code is public): https://huggingface.co/spaces/gradio/oauth-example

Will try AWS tomorrow!

abidlabs commented 3 months ago

Just FYI @pseudotensor I was able to set it up on AWS behind an nginx proxy. The code for the fastapi app is exactly the same as in the Space I linked above.

Live demo: http://35.85.61.147.nip.io/main/

My nginx conf looks like this:

server {
    listen 80;
    server_name example.com www.example.com;  # Change this to your domain name if you have one

    location / {  # Change this if you'd like to server your Gradio app on a different path
        proxy_pass http://127.0.0.1:7860/; # Change this if your Gradio app will be running on a different port
        proxy_buffering off;
        proxy_redirect off;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}
pseudotensor commented 3 months ago

Thanks. Is that a case where the AWS machine has separate IP inside than outside?

Also, is the code the same as earlier? Or did you change things for spaces case? I'll check.

I'll see what is up with our settings for nginx or whatever.

abidlabs commented 3 months ago

Yes, the public IP address is 35.85.61.147, but that is only needed in your Google Developer Console to allow the redirects. This is what my GDC settings look like:

image
chadbr commented 2 months ago

@abidlabs awesome - can you clarify - does the google oauth only work with huggingface spaces? Or could I go directly against the google oauth provider?

abidlabs commented 2 months ago

Hi @chadbr Google OAuth will work for all Gradio apps, regardless of whether they are hosted on Spaces. If you take the code here and run it locally, it should work as well: https://huggingface.co/spaces/gradio/oauth-example

EshamAaqib commented 2 months ago

Thanks. Is that a case where the AWS machine has separate IP inside than outside?

Also, is the code the same as earlier? Or did you change things for spaces case? I'll check.

I'll see what is up with our settings for nginx or whatever.

Yes, the public IP address is 35.85.61.147, but that is only needed in your Google Developer Console to allow the redirects. This is what my GDC settings look like:

image

Hi @abidlabs,

Regarding @pseudotensor 's setup. We have it exposed to the public via an AWS ALB (SSL Cert attached so https) which goes through Nginx proxy (http), when we try to initiate a login via Google Auth the redirect URI it returns is http://, Is there any way we could force Gradio to return a https:// URI. Google auth does not allow using http:// URIs in Production mode

EshamAaqib commented 2 months ago

Thanks. Is that a case where the AWS machine has separate IP inside than outside? Also, is the code the same as earlier? Or did you change things for spaces case? I'll check. I'll see what is up with our settings for nginx or whatever.

Yes, the public IP address is 35.85.61.147, but that is only needed in your Google Developer Console to allow the redirects. This is what my GDC settings look like:

image

Hi @abidlabs,

Regarding @pseudotensor 's setup. We have it exposed to the public via an AWS ALB (SSL Cert attached so https) which goes through Nginx proxy (http), when we try to initiate a login via Google Auth the redirect URI it returns is http://, Is there any way we could force Gradio to return a https:// URI. Google auth does not allow using http:// URIs in Production mode

UPDATE: Was able to force it to use https:// by modifying this, Working now

from urllib.parse import urlparse, urlunparse

@app.route('/login')
async def login(request: Request):
    parsed_url = urlparse(str(request.url_for('auth')))
    modified_url = parsed_url._replace(scheme='https')
    redirect_uri = urlunparse(modified_url)
    return await oauth.google.authorize_redirect(request, redirect_uri)
abidlabs commented 2 months ago

Thanks @EshamAaqib yes that works. I do something similar here: https://huggingface.co/spaces/gradio/oauth-example/blob/6c746b5828864cb057ed5d9a9fe725d91d6723fa/app.py#L53

gr.route_utils.get_root_url gets the root url and adds https if running on https (but I guess better to be explicit about this since get_root_url is an internal functions and could change)

pseudotensor commented 2 months ago

@abidlabs Does the code in gradio have this fixed so handles generally? Or have to tweak this wrapper code every time for either http or https cases?

pseudotensor commented 2 months ago

@abidlabs For this google auth solution, seems to be better to avoid having to mount things and be just a part of gradio. Not like google is a random thing, everyone uses it for auth. So better to be in gradio as option not as external mount. That way no issues with rest of usage, like options in .launch

abidlabs commented 2 months ago

@abidlabs For this google auth solution, seems to be better to avoid having to mount things and be just a part of gradio. Not like google is a random thing, everyone uses it for auth. So better to be in gradio as option not as external mount. That way no issues with rest of usage, like options in .launch

Not really possible, because you need to define other routes to handle the login page, auth page, etc. I considered other options, but its better to provide a template and then let users define these routes so that they can modify them as needed rather than hide them behind a layer of abstraction.

pseudotensor commented 2 months ago

What about all the launch parameters? Can can one pass all of those through to gradio?