auth/implementing-oauth/SKILL.md
GitHub and Google OAuth 2.0 integration using Authlib for Flask applications.
npx skillsauth add 7a336e6e/skills implementing-oauthInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Integrate OAuth 2.0 Authorization Code flow for GitHub and Google using Authlib. Support first-time login (account creation) and account linking for users who already have a local account with a matching email.
state parameter.code and state.state, exchanges the code for an access token.from authlib.integrations.flask_client import OAuth
from flask import Flask
app = Flask(__name__)
app.config.from_prefixed_env()
oauth = OAuth(app)
oauth.register(
name="github",
client_id=app.config["GITHUB_CLIENT_ID"],
client_secret=app.config["GITHUB_CLIENT_SECRET"],
authorize_url="https://github.com/login/oauth/authorize",
access_token_url="https://github.com/login/oauth/access_token",
api_base_url="https://api.github.com/",
client_kwargs={"scope": "read:user user:email"},
)
oauth.register(
name="google",
client_id=app.config["GOOGLE_CLIENT_ID"],
client_secret=app.config["GOOGLE_CLIENT_SECRET"],
authorize_url="https://accounts.google.com/o/oauth2/v2/auth",
access_token_url="https://oauth2.googleapis.com/token",
api_base_url="https://www.googleapis.com/",
server_metadata_url="https://accounts.google.com/.well-known/openid-configuration",
client_kwargs={"scope": "openid email profile"},
)
from flask import Blueprint, redirect, url_for, session, jsonify
oauth_bp = Blueprint("oauth", __name__, url_prefix="/api/v1/auth")
@oauth_bp.route("/github/login")
def github_login():
redirect_uri = url_for("oauth.github_callback", _external=True)
return oauth.github.authorize_redirect(redirect_uri)
@oauth_bp.route("/github/callback")
def github_callback():
token = oauth.github.authorize_access_token()
resp = oauth.github.get("user", token=token)
profile = resp.json()
# Fetch email separately if not public
if not profile.get("email"):
emails_resp = oauth.github.get("user/emails", token=token)
emails = emails_resp.json()
primary = next((e for e in emails if e["primary"] and e["verified"]), None)
if primary:
profile["email"] = primary["email"]
user = link_or_create_user(
provider="github",
provider_id=str(profile["id"]),
email=profile.get("email"),
name=profile.get("name") or profile.get("login"),
)
access_token = generate_access_token(user)
return jsonify({"access_token": access_token}), 200
When an OAuth user logs in, attempt to match their verified email to an existing local account. If a match is found, link the OAuth identity to that account rather than creating a duplicate.
def link_or_create_user(provider: str, provider_id: str, email: str, name: str):
# Check for existing OAuth link
oauth_account = OAuthAccount.query.filter_by(
provider=provider, provider_id=provider_id
).first()
if oauth_account:
return oauth_account.user
# Check for existing user by email
user = User.query.filter_by(email=email).first() if email else None
if not user:
user = User(email=email, name=name)
db.session.add(user)
# Link OAuth identity
oauth_account = OAuthAccount(
user=user, provider=provider, provider_id=provider_id
)
db.session.add(oauth_account)
db.session.commit()
return user
The Google flow follows the same pattern as GitHub. The key difference is that Google returns the email directly in the user info response and supports OpenID Connect.
@oauth_bp.route("/google/login")
def google_login():
redirect_uri = url_for("oauth.google_callback", _external=True)
return oauth.google.authorize_redirect(redirect_uri)
@oauth_bp.route("/google/callback")
def google_callback():
token = oauth.google.authorize_access_token()
resp = oauth.google.get("oauth2/v3/userinfo", token=token)
profile = resp.json()
user = link_or_create_user(
provider="google",
provider_id=profile["sub"],
email=profile.get("email"),
name=profile.get("name"),
)
access_token = generate_access_token(user)
return jsonify({"access_token": access_token}), 200
state parameter on every callback to prevent CSRF attacks.OAuth login redirect (302): Redirect to the provider's authorization page.
Callback success (200):
{ "access_token": "<jwt>" }
Callback failure (401):
{ "error": "OAuth authentication failed" }
development
Implement features using the Red-Green-Refactor cycle to ensure testability and correctness from the start.
data-ai
Manage the `tasks.md` ledger with strict locking and collision avoidance protocols to allow multiple agents to work in parallel safely.
development
The git-workflow skill defines branching conventions, commit message formats, and pull request standards that all agents must follow for consistent version control.
development
The environment-config skill standardizes how agents manage environment variables, secrets, and application configuration across local development and deployed environments.