Web application using Google Calendar API

Viewed 125

I've created a simple Python/flask website which connects to a Google Calendar via the Google Calendar API. I followed the instructions at https://developers.google.com/calendar/api/quickstart/python, and it works well...

The whole thing is internal to my company.

I copied the credentials.json and token.json along with my website to an internal device I'm using as a webserver. Again, all works fine.

Except, at some stage, I am under the impression that my credentials will expire, and the web server will need to re-authenticate with my personal google account.

How do I get around this?

Thanks

2 Answers

The reason your refresh token is expiring is that your app is still in the testing phase. Go to google cloud console under the consent screen and set it to production. Your tokens will stop expiring.

web app vs installed app

You should note is the sample you are following states

enter image description here

and uses the code for an installed application

flow = InstalledAppFlow.from_client_secrets_file(
            'credentials.json', SCOPES)

So by following this example you are not creating a web application you are creating an installed application.

Using flask you can design the following scheme.

  • You should have a /auth endpoint that generates authorization_url that you can redirect to perform authorization in Google API.
  • You should have a /callback endpoint that handles requests when your authorization in Google API is completed. In this callback, you can store your credentials in a flask session.
  • Before making events request, you should check whether your stored credentials are still valid. If not, you should call /auth again.

In GCP app console, when you create credentials for your app you should choose "web application". enter image description here

from flask import Flask, redirect, request, url_for
from google_auth_oauthlib.flow import Flow

app = Flask(__name__)
app.secret_key = os.environ.get("SECRET_KEY")
app.config["SESSION_TYPE"] = "filesystem"

# this callback URL should match one saved in GCP app console "Authorized redirection URIs" section 
CALLBACK_URL = os.environ.get("CALLBACK_URL") # you can use `url_for('callback')` instead
API_CLIENT_ID = os.environ.get("API_CLIENT_ID")
API_CLIENT_SECRET = os.environ.get("API_CLIENT_SECRET")
SCOPES = ["https://www.googleapis.com/auth/calendar"]

class CalendarClient:
    API_SERVICE = "calendar"
    API_VERSION = "v3"

    def __init__(self, client_id: str, client_secret: str, scopes: Sequence[str]):
        self._client_id = client_id
        self._client_secret = client_secret
        self._scopes = scopes
        self._client_config = {
            "web": {
                "client_id": client_id,
                "client_secret": client_secret,
                "auth_uri": "https://accounts.google.com/o/oauth2/auth",
                "token_uri": "https://oauth2.googleapis.com/token",
            }
        }

    def get_flow(self, callback_url: str) -> Flow:
        return Flow.from_client_config(
            self._client_config, self._scopes, redirect_uri=callback_url
        )

    def get_auth_url(self, callback_url: str) -> str:
        flow = self.get_flow(callback_url)
        auth_url, _ = flow.authorization_url(
            access_type="offline", include_granted_scopes="true"
        )
        return auth_url

    def get_credentials(self, code: str, callback_url: str) -> Credentials:
        flow = self.get_flow(callback_url)
        flow.fetch_token(code=code)
        return flow.credentials

@app.route("/callback")
def callback():
    credentials = client.get_credentials(
        code=request.args.get("code"),
        callback_url=CALLBACK_URL,
    )
    session["credentials"] = {
        "token": credentials.token,
        "refresh_token": credentials.refresh_token,
        "token_uri": credentials.token_uri,
        "client_id": credentials.client_id,
        "client_secret": credentials.client_secret,
        "scopes": credentials.scopes,
    }
    return credentials.to_json()


@app.route("/auth")
def auth():
    return redirect(client.get_auth_url(CALLBACK_URL))

Full codebase: https://github.com/jorzel/flask-google-calendar

Related