From 6d796c95ff0b532eaf4d1f9db341581e3f93b7db Mon Sep 17 00:00:00 2001 From: Christian Krakau-Louis Date: Tue, 15 Apr 2025 14:28:14 +0200 Subject: [PATCH] feat: Add NetID OAuth provider integration and update login UI - Implemented NetID OAuth provider in app/auth/oauth.py - Added styles for NetID login button in app/static/css/styles.css - Updated login.html to include NetID login option with custom button - Modified docker-compose.yml to include NetID client credentials - Enhanced social login setup documentation to include NetID instructions - Created demo environment configuration file (.env.demo) for easier setup - Updated .env.template to include NetID configuration options - Added comprehensive README.md with project features and setup instructions --- .env.demo | 74 ++++++++ .env.template | 72 ++++++++ README.md | 58 ++++++ app/auth/oauth.py | 108 ++++++++++++ app/static/css/styles.css | 323 +++++++++++++++++++++++++++++++++- app/templates/auth/login.html | 103 ++++++++--- docker-compose.yml | 14 +- docs/social_login_setup.md | 290 +++++++++++++++--------------- 8 files changed, 863 insertions(+), 179 deletions(-) create mode 100644 .env.demo create mode 100644 .env.template create mode 100644 README.md diff --git a/.env.demo b/.env.demo new file mode 100644 index 0000000..30e7571 --- /dev/null +++ b/.env.demo @@ -0,0 +1,74 @@ +# LeagueLedger Demo Environment Configuration +# This file contains example values for development/demo purposes +# DO NOT use these values in production! + +# Database Configuration +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=leagueledger_demo +DB_USER=leagueledger_user +DB_PASSWORD=demo_password_123 + +# Application Settings +SECRET_KEY=demo-secret-key-replace-in-production-e8a1b2c3d4e5f6 +DEBUG=True +ALLOWED_HOSTS=localhost,127.0.0.1,demo.leagueledger.com +BASE_URL=http://localhost:8000 +TIMEZONE=Europe/Dublin + +# Email Configuration +EMAIL_BACKEND=smtp +EMAIL_HOST=smtp.mailgun.org +EMAIL_PORT=587 +EMAIL_USE_TLS=True +EMAIL_HOST_USER=postmaster@demo.leagueledger.com +EMAIL_HOST_PASSWORD=demo_mail_password_123 +DEFAULT_FROM_EMAIL=noreply@demo.leagueledger.com + +# Security Settings +# These are set to False for local development +SESSION_COOKIE_SECURE=False +CSRF_COOKIE_SECURE=False +SECURE_BROWSER_XSS_FILTER=True +SECURE_CONTENT_TYPE_NOSNIFF=True + +# OAuth Providers - Example values (these are not real credentials) +# Google +GOOGLE_CLIENT_ID=123456789012-abcdefghijklmnopqrstuvwxyz123456.apps.googleusercontent.com +GOOGLE_CLIENT_SECRET=GOCSPX-abcdefghijklmnopqrstuvwxyz12345 + +# Facebook +FACEBOOK_CLIENT_ID=1234567890123456 +FACEBOOK_CLIENT_SECRET=abcdef0123456789abcdef0123456789 + +# GitHub +GITHUB_CLIENT_ID=Iv1.abcdef1234567890 +GITHUB_CLIENT_SECRET=abcdef1234567890abcdef1234567890abcdef12 + +# LinkedIn +LINKEDIN_CLIENT_ID=abcdefghij1234567890 +LINKEDIN_CLIENT_SECRET=ABCDEfghij1234567890abcdefghijklmno + +# Microsoft +MICROSOFT_CLIENT_ID=12345678-1234-1234-1234-123456789012 +MICROSOFT_CLIENT_SECRET=abc~DEF.ghiJKLmnoPQRstuvwxyz_12345678 +MICROSOFT_TENANT=common + +# Discord +DISCORD_CLIENT_ID=123456789012345678 +DISCORD_CLIENT_SECRET=abcDEFghi1234567890JKLMNO_pqrSTUvwxyz + +# Authentik +AUTHENTIK_CLIENT_ID=yourAuthentikClientID +AUTHENTIK_CLIENT_SECRET=yourAuthentikClientSecret +AUTHENTIK_CONFIG_URL=https://authentik.example.com/application/o/leagueledger/.well-known/openid-configuration + +# NetID +NETID_CLIENT_ID=leagueledger-netid-client +NETID_CLIENT_SECRET=netid-client-secret-abcdef1234567890 + +# Feature Flags +ENABLE_REGISTRATION=True +ENABLE_PASSWORD_RESET=True +ENABLE_EMAIL_VERIFICATION=True +ENABLE_SOCIAL_LOGIN=True \ No newline at end of file diff --git a/.env.template b/.env.template new file mode 100644 index 0000000..fea5103 --- /dev/null +++ b/.env.template @@ -0,0 +1,72 @@ +# LeagueLedger Environment Configuration Template +# Copy this file to .env and fill in the values + +# Database Configuration +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=leagueledger +DB_USER=postgres +DB_PASSWORD= + +# Application Settings +SECRET_KEY= +DEBUG=False +ALLOWED_HOSTS=localhost,127.0.0.1 +BASE_URL=http://localhost:8000 +TIMEZONE=Europe/Dublin + +# Email Configuration +EMAIL_BACKEND=smtp +EMAIL_HOST=smtp.example.com +EMAIL_PORT=587 +EMAIL_USE_TLS=True +EMAIL_HOST_USER= +EMAIL_HOST_PASSWORD= +DEFAULT_FROM_EMAIL=noreply@leagueledger.com + +# Security Settings +SESSION_COOKIE_SECURE=True +CSRF_COOKIE_SECURE=True +SECURE_BROWSER_XSS_FILTER=True +SECURE_CONTENT_TYPE_NOSNIFF=True + +# OAuth Providers +# Google +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= + +# Facebook +FACEBOOK_CLIENT_ID= +FACEBOOK_CLIENT_SECRET= + +# GitHub +GITHUB_CLIENT_ID= +GITHUB_CLIENT_SECRET= + +# LinkedIn +LINKEDIN_CLIENT_ID= +LINKEDIN_CLIENT_SECRET= + +# Microsoft +MICROSOFT_CLIENT_ID= +MICROSOFT_CLIENT_SECRET= +MICROSOFT_TENANT=common + +# Discord +DISCORD_CLIENT_ID= +DISCORD_CLIENT_SECRET= + +# Authentik +AUTHENTIK_CLIENT_ID= +AUTHENTIK_CLIENT_SECRET= +AUTHENTIK_CONFIG_URL= + +# NetID +NETID_CLIENT_ID= +NETID_CLIENT_SECRET= + +# Feature Flags +ENABLE_REGISTRATION=True +ENABLE_PASSWORD_RESET=True +ENABLE_EMAIL_VERIFICATION=True +ENABLE_SOCIAL_LOGIN=True \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..67a3eb3 --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +# LeagueLedger LeagueLedger Logo + +A modern web application for managing sports league loyalty programs, events, and rewards through QR codes. + +## Features + +- **Team Management**: Create and manage teams with member profiles +- **QR Code Redemption**: Generate and scan QR codes for points and rewards +- **Leaderboards**: Track team and individual standings +- **OAuth Integration**: Multiple social login options including Google, GitHub, LinkedIn, Facebook, and netID +- **Internationalization**: Multi-language support with English and German locales +- **Responsive Design**: Mobile-friendly interface built with Tailwind CSS + +## Quick Start + +1. **Clone the repository** + ``` + git clone https://github.com/yourusername/LeagueLedger.git + cd LeagueLedger + ``` + +2. **Set up environment variables** + ``` + cp .env.template .env + # Edit .env with your configuration + ``` + +3. **Run with Docker** + ``` + docker-compose up -d + ``` + +4. **Or run locally** + ``` + pip install -r requirements.txt + python app/main.py + ``` + +5. **Access the application** + ``` + http://localhost:8000 + ``` + +## Documentation + +Detailed documentation is available in the `/docs` directory. Generate the complete documentation site with: + +``` +mkdocs serve +``` + +## License + +This project is licensed under the terms of the license file included in this repository. + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. \ No newline at end of file diff --git a/app/auth/oauth.py b/app/auth/oauth.py index 5b6f3fa..84ed1ac 100644 --- a/app/auth/oauth.py +++ b/app/auth/oauth.py @@ -818,6 +818,113 @@ class LinkedInOAuth(OAuthProvider): "raw": user_info } +class NetIDOAuth(OAuthProvider): + """NetID OAuth provider implementation""" + + provider_id = "netid" + display_name = "netID" + icon_class = "fas fa-check" # Could be replaced with a custom netID icon class if available + button_color = "#76b82a" + + # NetID OIDC endpoints + AUTHORIZATION_URL = "https://broker.netid.de/authorize" + TOKEN_URL = "https://broker.netid.de/token" + USERINFO_URL = "https://broker.netid.de/userinfo" + + def __init__(self): + self.client_id = os.getenv("NETID_CLIENT_ID", "") + self.client_secret = os.getenv("NETID_CLIENT_SECRET", "") + super().__init__() + + def initialize_client(self): + if not self.client_id or not self.client_secret: + self.client = None + return + + try: + # Create a custom OAuth2 client for NetID OpenID Connect + from httpx_oauth.oauth2 import OAuth2 + self.client = OAuth2( + client_id=self.client_id, + client_secret=self.client_secret, + authorize_endpoint=self.AUTHORIZATION_URL, + access_token_endpoint=self.TOKEN_URL, + refresh_token_endpoint=self.TOKEN_URL, + base_scopes=["openid", "email", "profile"] + ) + except Exception as e: + print(f"Error initializing NetID OAuth client: {str(e)}") + self.client = None + + async def get_login_url(self, request: Request, redirect_uri: str) -> str: + if not self.client: + self.initialize_client() + + if not self.client: + raise HTTPException(status_code=500, detail="NetID OAuth client could not be initialized") + + try: + authorization_url = await self.client.get_authorization_url( + redirect_uri=redirect_uri, + scope=["openid", "email", "profile"], + state=str(request.session.get("session_id", "")) + ) + return authorization_url + except Exception as e: + print(f"Error getting NetID authorization URL: {str(e)}") + raise HTTPException(status_code=500, detail=f"OAuth error: {str(e)}") + + async def get_user_info(self, request: Request, redirect_uri: str, code: str) -> Dict[str, Any]: + if not self.client: + self.initialize_client() + + if not self.client: + raise HTTPException(status_code=500, detail="NetID OAuth client could not be initialized") + + try: + # Exchange code for token + token = await self.client.get_access_token( + code=code, + redirect_uri=redirect_uri + ) + + access_token = token.get("access_token") + + if not access_token: + raise HTTPException(status_code=400, detail="Could not get NetID access token") + + # Get user info from NetID UserInfo endpoint + async with httpx.AsyncClient() as client: + headers = {"Authorization": f"Bearer {access_token}"} + response = await client.get( + self.USERINFO_URL, + headers=headers + ) + + if response.status_code != 200: + raise HTTPException(status_code=500, detail=f"Error fetching NetID user info: {response.text}") + + return response.json() + + except GetAccessTokenError as e: + error_description = e.args[0] + raise HTTPException(status_code=400, detail=f"NetID OAuth error: {error_description}") + except Exception as e: + print(f"Error getting NetID user info: {str(e)}") + raise HTTPException(status_code=500, detail=f"OAuth error: {str(e)}") + + def get_normalized_user_data(self, user_info: Dict[str, Any]) -> Dict[str, Any]: + # NetID OpenID Connect response normalization + return { + "id": user_info.get("sub", ""), # 'sub' is the standard OIDC subject identifier + "email": user_info.get("email"), + "name": f"{user_info.get('given_name', '')} {user_info.get('family_name', '')}".strip(), + "first_name": user_info.get("given_name"), + "last_name": user_info.get("family_name"), + "picture": None, # NetID might not provide profile picture + "raw": user_info + } + class OAuthManager: """ Manager class for handling multiple OAuth providers @@ -836,6 +943,7 @@ class OAuthManager: self.register_provider(MicrosoftOAuth()) self.register_provider(DiscordOAuth()) self.register_provider(LinkedInOAuth()) + self.register_provider(NetIDOAuth()) # Register NetID provider def register_provider(self, provider: OAuthProvider): """Register a new provider""" diff --git a/app/static/css/styles.css b/app/static/css/styles.css index f6e753c..613799f 100644 --- a/app/static/css/styles.css +++ b/app/static/css/styles.css @@ -1,7 +1,7 @@ /* * LeagueLedger Main Stylesheet * Version: 1.0.0 - * Date: April 13, 2025 + * Date: April 15, 2025 */ :root { @@ -155,4 +155,325 @@ h1, h2, h3, h4, h5, h6 { font-weight: bold; color: var(--irish-green); } +} + +/* Google Sign-In Button Styles */ +.gsi-material-button { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + -webkit-appearance: none; + background-color: WHITE; + background-image: none; + border: 1px solid #747775; + -webkit-border-radius: 4px; + border-radius: 4px; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: #1f1f1f; + cursor: pointer; + font-family: 'Roboto', arial, sans-serif; + font-size: 14px; + height: 40px; + letter-spacing: 0.25px; + outline: none; + overflow: hidden; + padding: 0 12px; + position: relative; + text-align: center; + -webkit-transition: background-color .218s, border-color .218s, box-shadow .218s; + transition: background-color .218s, border-color .218s, box-shadow .218s; + vertical-align: middle; + white-space: nowrap; + width: auto; + max-width: 400px; + min-width: min-content; + } + +.gsi-material-button .gsi-material-button-icon { + height: 20px; + margin-right: 12px; + min-width: 20px; + width: 20px; +} + +.gsi-material-button .gsi-material-button-content-wrapper { + -webkit-align-items: center; + align-items: center; + display: flex; + -webkit-flex-direction: row; + flex-direction: row; + -webkit-flex-wrap: nowrap; + flex-wrap: nowrap; + height: 100%; + justify-content: space-between; + position: relative; + width: 100%; +} + +.gsi-material-button .gsi-material-button-contents { + -webkit-flex-grow: 1; + flex-grow: 1; + font-family: 'Roboto', arial, sans-serif; + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: top; +} + +.gsi-material-button .gsi-material-button-state { + -webkit-transition: opacity .218s; + transition: opacity .218s; + bottom: 0; + left: 0; + opacity: 0; + position: absolute; + right: 0; + top: 0; +} + +.gsi-material-button:disabled { + cursor: default; + background-color: #ffffff61; + border-color: #1f1f1f1f; +} + +.gsi-material-button:disabled .gsi-material-button-contents { + opacity: 38%; +} + +.gsi-material-button:disabled .gsi-material-button-icon { + opacity: 38%; +} + +.gsi-material-button:not(:disabled):active .gsi-material-button-state, +.gsi-material-button:not(:disabled):focus .gsi-material-button-state { + background-color: #303030; + opacity: 12%; +} + +.gsi-material-button:not(:disabled):hover { + -webkit-box-shadow: 0 1px 2px 0 rgba(60, 64, 67, .30), 0 1px 3px 1px rgba(60, 64, 67, .15); + box-shadow: 0 1px 2px 0 rgba(60, 64, 67, .30), 0 1px 3px 1px rgba(60, 64, 67, .15); +} + +.gsi-material-button:not(:disabled):hover .gsi-material-button-state { + background-color: #303030; + opacity: 8%; +} + +/* OAuth Buttons Container */ +.oauth-buttons-container { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + max-width: 280px; + margin: 0 auto; +} + +/* LinkedIn Login Button */ +.linkedin-login-button { + background-color: #0077B5; + border: none; + border-radius: 4px; + color: white; + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-size: 14px; + font-weight: 600; + height: 40px; + letter-spacing: normal; + padding: 0; + position: relative; + text-align: center; + transition: background-color .218s; + width: 100%; + max-width: 280px; + margin: 0 auto 8px; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; +} + +.linkedin-login-button:hover { + background-color: #006097; +} + +.linkedin-login-button .linkedin-icon { + display: inline-block; + margin-right: 8px; + height: 18px; + width: 18px; +} + +.linkedin-login-button .linkedin-icon svg { + height: 18px; + width: 18px; + fill: white; +} + +.linkedin-login-button .button-text { + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-size: 14px; + font-weight: 600; +} + +/* Facebook Login Button */ +.fb-login-button { + background-color: #1877F2; + border: none; + border-radius: 4px; + color: white; + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-size: 14px; + font-weight: 600; + height: 40px; + letter-spacing: normal; + padding: 0; + position: relative; + text-align: center; + transition: background-color .218s; + width: 100%; + max-width: 280px; + margin: 0 auto 8px; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; +} + +.fb-login-button:hover { + background-color: #166FE5; +} + +.fb-login-button .fb-icon { + display: inline-block; + margin-right: 8px; + height: 18px; + width: 18px; + background-color: white; + border-radius: 2px; +} + +.fb-login-button .fb-icon svg { + height: 18px; + width: 18px; + fill: #1877F2; +} + +.fb-login-button .button-text { + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-size: 14px; + font-weight: 600; +} + +/* GitHub Login Button */ +.github-login-button { + background-color: #24292e; + border: 1px solid rgba(27, 31, 35, 0.15); + border-radius: 6px; + color: white; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif; + font-size: 14px; + font-weight: 600; + height: 40px; + padding: 0; + position: relative; + text-align: center; + transition: background-color 0.2s; + width: 100%; + max-width: 280px; + margin: 0 auto 8px; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; +} + +.github-login-button:hover { + background-color: #2c3136; +} + +.github-login-button .github-icon { + display: inline-block; + margin-right: 8px; + height: 20px; + width: 20px; +} + +.github-login-button .github-icon svg { + height: 20px; + width: 20px; + fill: white; +} + +.github-login-button .button-text { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif; + font-size: 14px; + font-weight: 600; +} + +/* NetID Login Button */ +.netid-login-button { + background-color: #76b82a; + border: none; + border-radius: 3px; + color: white; + font-family: 'IBM Plex Sans', Verdana, Arial, sans-serif; + font-size: 14px; + font-weight: 600; + height: 40px; + padding: 0; + position: relative; + text-align: center; + transition: background-color 0.2s; + width: 100%; + max-width: 280px; + margin: 0 auto 8px; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; +} + +.netid-login-button:hover { + background-color: #5d9422; +} + +.netid-login-button .netid-icon { + display: inline-block; + margin-right: 8px; + height: 20px; + width: 20px; +} + +.netid-login-button .netid-icon svg { + height: 20px; + width: 20px; + fill: white; +} + +.netid-login-button .button-text { + font-family: 'IBM Plex Sans', Verdana, Arial, sans-serif; + font-size: 14px; + font-weight: 600; +} + +/* Other OAuth provider button customizations */ +.oauth-button { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + padding: 8px 16px; + border-radius: 4px; + font-weight: 500; + transition: all 0.2s ease; + margin-bottom: 8px; + text-decoration: none; } \ No newline at end of file diff --git a/app/templates/auth/login.html b/app/templates/auth/login.html index 131d0fe..d3973c2 100644 --- a/app/templates/auth/login.html +++ b/app/templates/auth/login.html @@ -91,30 +91,91 @@

Or sign in with

- - {% if oauth_providers|length <= 2 %} -
+
{% for provider in oauth_providers %} - - {{ provider.name }} - + {% if provider.id == 'google' %} + + + + + {% elif provider.id == 'facebook' %} + + + + + {% elif provider.id == 'linkedin' %} + + + + + {% elif provider.id == 'github' %} + + + + + {% elif provider.id == 'netid' %} + +
+

Login with GMX, WEB.DE, or other email providers via netID

+
+ + + + + {% else %} + + + Sign in with {{ provider.name }} + + {% endif %} {% endfor %}
- - - {% else %} -
- {% for provider in oauth_providers %} - - {{ provider.name }} - - {% endfor %} -
- {% endif %}
{% endif %}
diff --git a/docker-compose.yml b/docker-compose.yml index c59c391..5218c65 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -70,17 +70,21 @@ services: FACEBOOK_CLIENT_SECRET: ${FACEBOOK_CLIENT_SECRET:-} # Microsoft OAuth - MICROSOFT_CLIENT_ID: ${MICROSOFT_CLIENT_ID:-} - MICROSOFT_CLIENT_SECRET: ${MICROSOFT_CLIENT_SECRET:-} - MICROSOFT_TENANT: ${MICROSOFT_TENANT:-common} + #MICROSOFT_CLIENT_ID: ${MICROSOFT_CLIENT_ID:-} + #MICROSOFT_CLIENT_SECRET: ${MICROSOFT_CLIENT_SECRET:-} + #MICROSOFT_TENANT: ${MICROSOFT_TENANT:-common} # Discord OAuth - DISCORD_CLIENT_ID: ${DISCORD_CLIENT_ID:-} - DISCORD_CLIENT_SECRET: ${DISCORD_CLIENT_SECRET:-} + #DISCORD_CLIENT_ID: ${DISCORD_CLIENT_ID:-} + #DISCORD_CLIENT_SECRET: ${DISCORD_CLIENT_SECRET:-} # LinkedIn OAuth LINKEDIN_CLIENT_ID: ${LINKEDIN_CLIENT_ID:-} LINKEDIN_CLIENT_SECRET: ${LINKEDIN_CLIENT_SECRET:-} + + NETID_CLIENT_ID: ${NETID_CLIENT_ID:-} + NETID_CLIENT_SECRET: ${NETID_CLIENT_SECRET:-} + command: uvicorn app.main:app --host 0.0.0.0 --reload ports: - "8000:8000" diff --git a/docs/social_login_setup.md b/docs/social_login_setup.md index fdea28f..86b4b57 100644 --- a/docs/social_login_setup.md +++ b/docs/social_login_setup.md @@ -1,54 +1,17 @@ -# Setting Up Social Login in LeagueLedger +# Social Login Setup -LeagueLedger supports multiple social login (OAuth) providers to give your users various options for authentication. This document explains how to set up each supported provider. +This guide provides instructions for setting up various social login providers for LeagueLedger. ## Table of Contents -1. [General Setup](#general-setup) -2. [Callback URLs](#callback-urls) -3. [Provider-Specific Instructions](#provider-specific-instructions) - - [Google](#google) - - [GitHub](#github) - - [Facebook](#facebook) - - [Microsoft](#microsoft) - - [Discord](#discord) - - [LinkedIn](#linkedin) - - [Authentik](#authentik) -4. [Troubleshooting](#troubleshooting) +- [Google OAuth Setup](#google-oauth-setup) +- [Facebook Login Setup](#facebook-login-setup) +- [GitHub OAuth Setup](#github-oauth-setup) +- [LinkedIn OAuth Setup](#linkedin-oauth-setup) +- [Microsoft OAuth Setup](#microsoft-oauth-setup) +- [Discord OAuth Setup](#discord-oauth-setup) +- [NetID OAuth Setup](#netid-oauth-setup) -## General Setup - -To enable social login in LeagueLedger, you need to: - -1. Register your application with the desired OAuth provider(s) -2. Obtain client ID and client secret credentials -3. Add these credentials to your environment variables or `.env` file -4. Restart the application - -Only providers with valid credentials will appear on the login page. - -## Callback URLs - -Each OAuth provider requires you to configure a **Redirect URI** (also known as a callback URL). This is where the provider redirects users after they authenticate. - -For LeagueLedger, use the following pattern: -``` -https://your-domain.com/auth/oauth-callback/{provider_id} -``` - -Replace: -- `your-domain.com` with your actual domain -- `{provider_id}` with one of: `google`, `github`, `facebook`, `microsoft`, `discord`, `linkedin`, or `authentik` - -For local development, use: -``` -http://localhost:8000/auth/oauth-callback/{provider_id} -``` - -**Important:** Most OAuth providers require exact URL matches, including protocol (http/https), domain, path, and any query parameters. Make sure to register the exact URL as shown above. - -## Provider-Specific Instructions - -### Google +## Google OAuth Setup 1. Go to [Google Cloud Console](https://console.cloud.google.com/) 2. Create a new project or select an existing one @@ -68,26 +31,7 @@ http://localhost:8000/auth/oauth-callback/{provider_id} GOOGLE_CLIENT_SECRET=your-client-secret ``` -### GitHub - -1. Go to [GitHub Developer Settings](https://github.com/settings/developers) -2. Click "New OAuth App" -3. Fill in your application details: - - Application name: "LeagueLedger" - - Homepage URL: Your app's URL or `http://localhost:8000` - - Authorization callback URL: - ``` - http://localhost:8000/auth/oauth-callback/github - ``` -4. Click "Register application" -5. Generate a new client secret -6. Add to your `.env` file: - ``` - GITHUB_CLIENT_ID=your-client-id - GITHUB_CLIENT_SECRET=your-client-secret - ``` - -### Facebook +## Facebook Login Setup 1. Go to [Facebook Developers](https://developers.facebook.com/) 2. Create a new app (choose "Consumer" or "Business" type) @@ -103,48 +47,56 @@ http://localhost:8000/auth/oauth-callback/{provider_id} FACEBOOK_CLIENT_SECRET=your-app-secret ``` -### Microsoft +## GitHub OAuth Setup -1. Go to [Azure Portal](https://portal.azure.com/) -2. Navigate to "App registrations" -3. Click "New registration" -4. Enter a name for your application -5. For "Supported account types," choose an option based on your needs - (typically "Accounts in any organizational directory and personal Microsoft accounts") -6. Add the following Redirect URI (type: Web): - ``` - http://localhost:8000/auth/oauth-callback/microsoft - ``` -7. Click "Register" -8. Note the Application (client) ID -9. Create a client secret: Navigate to "Certificates & secrets" > "New client secret" -10. Add to your `.env` file: - ``` - MICROSOFT_CLIENT_ID=your-client-id - MICROSOFT_CLIENT_SECRET=your-client-secret - MICROSOFT_TENANT=common - ``` - Note: Use `common` for multi-tenant apps, or your specific tenant ID +### 1. Create a GitHub OAuth App -### Discord +1. Go to your GitHub account settings +2. Click on "Developer settings" in the left sidebar +3. Click on "OAuth Apps" and then "New OAuth App" +4. Fill out the form: + - **Application name**: LeagueLedger + - **Homepage URL**: Your site's URL (e.g. https://leagueledger.com) + - **Application description**: (Optional) A description of your app + - **Authorization callback URL**: Your callback URL (e.g. https://leagueledger.com/auth/oauth-callback/github) +5. Click "Register application" +6. You'll receive a Client ID +7. Click "Generate a new client secret" to create your Client Secret +8. Save both the Client ID and Client Secret safely -1. Go to the [Discord Developer Portal](https://discord.com/developers/applications) -2. Click "New Application" -3. Enter a name and click "Create" -4. Go to the "OAuth2" section in the left sidebar -5. Note the Client ID and generate a Client Secret -6. Add the following redirect URL: - ``` - http://localhost:8000/auth/oauth-callback/discord - ``` -7. In the "OAuth2 URL Generator" section, select the "identify" and "email" scopes -8. Add to your `.env` file: - ``` - DISCORD_CLIENT_ID=your-client-id - DISCORD_CLIENT_SECRET=your-client-secret - ``` +### 2. Configure Environment Variables -### LinkedIn +Add the following variables to your `.env` file: + +``` +GITHUB_CLIENT_ID=your_github_client_id +GITHUB_CLIENT_SECRET=your_github_client_secret +``` + +### 3. Security Considerations + +- **Never commit your Client Secret to version control** +- Store your Client Secret securely in environment variables or a secret management system +- In production, update the callback URL to use your production domain +- Consider implementing additional security measures like CSRF protection + +### 4. Testing GitHub Login + +After configuration: +1. Ensure the server is running with the environment variables loaded +2. Navigate to the login page +3. Click the "Login with GitHub" button +4. You should be redirected to GitHub's authorization page +5. After authorizing, you should be redirected back to your application and logged in + +### 5. Troubleshooting GitHub OAuth + +- **Invalid callback URL**: Ensure the callback URL registered in GitHub matches exactly what your application uses +- **Rate limiting**: GitHub has API rate limits that might affect your OAuth flow +- **Scope issues**: If you're not receiving email information, ensure you've requested the `user:email` scope +- **Token refresh**: If tokens expire, implement a refresh flow + +## LinkedIn OAuth Setup 1. Go to the [LinkedIn Developer Portal](https://www.linkedin.com/developers/) 2. Click "Create app" @@ -171,63 +123,97 @@ http://localhost:8000/auth/oauth-callback/{provider_id} LINKEDIN_CLIENT_SECRET=your-client-secret ``` -### Authentik +## Microsoft OAuth Setup -1. Access your Authentik admin interface -2. Go to "Applications" > "Providers" > "Create" -3. Select "OAuth2/OIDC Provider" -4. Configure the provider: - - Name: LeagueLedger - - Client Type: Confidential - - Redirect URIs: - ``` - http://localhost:8000/auth/oauth-callback/authentik - ``` - - Signing Key: Select an appropriate key or create one -5. Save the provider -6. Create an application: - - Go to "Applications" > "Applications" > "Create" - - Name: LeagueLedger - - Slug: leagueledger - - Provider: Select the provider you just created -7. Save the application -8. Note the Client ID and Client Secret -9. Add to your `.env` file: +1. Go to [Azure Portal](https://portal.azure.com/) +2. Navigate to "App registrations" +3. Click "New registration" +4. Enter a name for your application +5. For "Supported account types," choose an option based on your needs + (typically "Accounts in any organizational directory and personal Microsoft accounts") +6. Add the following Redirect URI (type: Web): ``` - AUTHENTIK_CLIENT_ID=your-client-id - AUTHENTIK_CLIENT_SECRET=your-client-secret - AUTHENTIK_CONFIG_URL=https://your-authentik-domain/application/o/leagueledger/.well-known/openid-configuration + http://localhost:8000/auth/oauth-callback/microsoft + ``` +7. Click "Register" +8. Note the Application (client) ID +9. Create a client secret: Navigate to "Certificates & secrets" > "New client secret" +10. Add to your `.env` file: + ``` + MICROSOFT_CLIENT_ID=your-client-id + MICROSOFT_CLIENT_SECRET=your-client-secret + MICROSOFT_TENANT=common + ``` + Note: Use `common` for multi-tenant apps, or your specific tenant ID + +## Discord OAuth Setup + +1. Go to the [Discord Developer Portal](https://discord.com/developers/applications) +2. Click "New Application" +3. Enter a name and click "Create" +4. Go to the "OAuth2" section in the left sidebar +5. Note the Client ID and generate a Client Secret +6. Add the following redirect URL: + ``` + http://localhost:8000/auth/oauth-callback/discord + ``` +7. In the "OAuth2 URL Generator" section, select the "identify" and "email" scopes +8. Add to your `.env` file: + ``` + DISCORD_CLIENT_ID=your-client-id + DISCORD_CLIENT_SECRET=your-client-secret ``` -## Troubleshooting +## NetID OAuth Setup -### Common Issues: +### 1. Create a NetID Service -1. **Provider not showing on login page** - - Check that client ID and secret are correctly set in your environment/`.env` file - - Verify that values are not empty strings - - Check application logs for initialization errors +1. Go to the [NetID Developer Zone](https://developer.netid.de/) +2. Create an account or log in with your existing credentials +3. Go to "Services" in the menu and click "Add service" +4. Fill in the required details: + - **Service domain**: Your site's domain (e.g., leagueledger.com) + - **URL privacy policy**: Link to your privacy policy + - **URL terms of usage**: Link to your terms of service + - Click "Add service" -2. **Authentication Error after provider login** - - Verify that the redirect URI is exactly as registered with the provider - - Check for protocol mismatch (http vs https) - - Ensure all required scopes have been granted +### 2. Create a NetID Client -3. **"Can't retrieve user email" errors** - - Ensure you've requested the email scope from the provider - - Some providers (like GitHub) require special permissions for email access +1. In your service's detail view, click "Add client" +2. Select the application type: + - For web application: select "Website" + - For mobile apps: select "Native / Mobile App (PKCE)" +3. Fill out the required fields: + - **Name**: "LeagueLedger" + - **Callback URL**: Your callback URL (e.g., https://leagueledger.com/auth/oauth-callback/netid) + - **Token signing**: Select "RS256" (recommended) +4. Save the client configuration +5. Note the Client ID and Client Secret -### Checking Provider Status: +### 3. Configure Environment Variables -You can check which providers are correctly configured by examining the login page: -- Only providers with valid credentials will appear as login options -- Look at application logs during startup for provider initialization messages +Add the following variables to your `.env` file: -### Provider-Specific Tips: +``` +NETID_CLIENT_ID=your_netid_client_id +NETID_CLIENT_SECRET=your_netid_client_secret +``` -- **Google**: Ensure the Google+ API is enabled in your Google Cloud project -- **GitHub**: For private email addresses, request the `user:email` scope -- **Discord**: Discord applications might need to be verified if you have a large user base -- **Microsoft**: Ensure the Microsoft Graph API permissions include User.Read +### 4. Testing -For more help, check the [official documentation](https://example.com/leagueledger/docs) or open an issue on the project repository. \ No newline at end of file +During development, you'll need to: +1. Add test users to your service in the NetID Developer Zone +2. Use these test users when testing the login functionality +3. Request production approval once your integration is ready + +### 5. Requesting Production Approval + +When ready for production: +1. Go to your service's details in the NetID Developer Zone +2. Click "Request service release" +3. NetID will review your integration and approve it for production use + +### 6. Helpful Resources + +- [NetID Technical Documentation](https://developer.netid.de/single-sign-on-integration/technical-details/) +- [NetID Styleguide](https://developer.netid.de/single-sign-on-integration/styleguide/) for button styling requirements \ No newline at end of file