diff --git a/README.md b/README.md index 53a64c0..102e081 100644 --- a/README.md +++ b/README.md @@ -65,12 +65,9 @@ response = descope_client.magiclink.sign_in_or_up( login_id="user@example.com" ) -# Extract tokens +# Extract session token session_token = response["sessionToken"]["jwt"] -refresh_token = response["refreshToken"]["jwt"] - print(f"export DESCOPE_SESSION_TOKEN='{session_token}'") -print(f"export DESCOPE_REFRESH_TOKEN='{refresh_token}'") ``` ### 3. Set Environment Variables @@ -79,7 +76,6 @@ print(f"export DESCOPE_REFRESH_TOKEN='{refresh_token}'") # Required export DESCOPE_PROJECT_ID="P2XXXXX" export DESCOPE_SESSION_TOKEN="" -export DESCOPE_REFRESH_TOKEN="" # Optional (with defaults) export DESCOPE_ADMIN_ROLES="admin,mlflow-admin" @@ -197,15 +193,14 @@ descope = "mlflow_descope_auth.context_provider:DescopeContextProvider" ## Configuration Reference -| Variable | Required | Default | Description | -| ------------------------- | -------- | -------------------- | ----------------------------------------------- | -| `DESCOPE_PROJECT_ID` | ✅ Yes | - | Your Descope Project ID | -| `DESCOPE_SESSION_TOKEN` | ✅ Yes | - | Current session JWT token | -| `DESCOPE_REFRESH_TOKEN` | ❌ No | - | Refresh token for automatic renewal | -| `DESCOPE_ADMIN_ROLES` | ❌ No | `admin,mlflow-admin` | Comma-separated list of admin roles | -| `DESCOPE_DEFAULT_PERMISSION` | ❌ No | `READ` | Default permission level (READ/EDIT/MANAGE) | -| `DESCOPE_USERNAME_CLAIM` | ❌ No | `sub` | JWT claim to use as username (`sub` or `email`) | -| `MLFLOW_TRACKING_AUTH` | ✅ Yes | - | Set to `descope` to enable plugin | +| Variable | Required | Default | Description | +| ---------------------------- | -------- | -------------------- | ----------------------------------------------- | +| `DESCOPE_PROJECT_ID` | ✅ Yes | - | Your Descope Project ID | +| `DESCOPE_SESSION_TOKEN` | ✅ Yes | - | Current session JWT token | +| `DESCOPE_ADMIN_ROLES` | ❌ No | `admin,mlflow-admin` | Comma-separated list of admin roles | +| `DESCOPE_DEFAULT_PERMISSION` | ❌ No | `READ` | Default permission level (READ/EDIT/MANAGE) | +| `DESCOPE_USERNAME_CLAIM` | ❌ No | `sub` | JWT claim to use as username (`sub` or `email`) | +| `MLFLOW_TRACKING_AUTH` | ✅ Yes | - | Set to `descope` to enable plugin | ## Architecture @@ -257,10 +252,7 @@ python -c " from mlflow_descope_auth import get_descope_client import os client = get_descope_client() -result = client.validate_session( - os.environ['DESCOPE_SESSION_TOKEN'], - os.environ.get('DESCOPE_REFRESH_TOKEN') -) +result = client.validate_session(os.environ['DESCOPE_SESSION_TOKEN']) print('✓ Token valid') " ``` diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md index 3c2217b..568b4b3 100644 --- a/docs/QUICKSTART.md +++ b/docs/QUICKSTART.md @@ -4,7 +4,7 @@ Get MLflow running with Descope authentication in 5 minutes! ## Prerequisites -- Python 3.8 or higher installed +- Python 3.9 or higher installed - pip package manager - A Descope account (free at [descope.com](https://www.descope.com/)) @@ -15,17 +15,32 @@ Get MLflow running with Descope authentication in 5 minutes! 3. Give it a name (e.g., "MLflow Auth") 4. Copy your **Project ID** (starts with `P2`) -## Step 2: Configure Authentication Flow +## Step 2: Get Authentication Tokens -1. In Descope Console, go to **Authentication → Flows** -2. You'll see a default `sign-up-or-in` flow - click to edit -3. Add your desired authentication methods: - - **OAuth**: Google, GitHub, Microsoft, etc. - - **Magic Link**: Passwordless email authentication - - **OTP**: SMS or email one-time password - - **Password**: Traditional username/password +Authenticate with Descope to obtain session tokens. You can use the Descope Python SDK: -4. Click **Publish** to save your flow +```python +from descope import DescopeClient + +descope_client = DescopeClient(project_id="P2XXXXX") + +# Authenticate user (example with magic link) +response = descope_client.magiclink.sign_in_or_up( + method="email", + login_id="user@example.com" +) + +# Extract session token +session_token = response["sessionToken"]["jwt"] +print(f"export DESCOPE_SESSION_TOKEN='{session_token}'") +``` + +Or use any other Descope authentication method (OAuth, SAML, etc.) via: +- [Descope Web SDK](https://docs.descope.com/build/guides/client_sdks/web/) +- [Descope Python SDK](https://docs.descope.com/build/guides/client_sdks/python/) +- Descope API directly + +> **Note**: Token refresh is handled automatically by the server, not via environment variables. ## Step 3: Install MLflow Descope Auth @@ -35,177 +50,170 @@ pip install mlflow-descope-auth ## Step 4: Configure Environment -Create a `.env` file in your project directory: +Set the required environment variables: ```bash -# Required: Your Descope Project ID -DESCOPE_PROJECT_ID=P2XXXXX +# Required +export DESCOPE_PROJECT_ID="P2XXXXX" +export DESCOPE_SESSION_TOKEN="" -# Optional: Flow ID (default is "sign-up-or-in") -DESCOPE_FLOW_ID=sign-up-or-in +# Enable the plugin +export MLFLOW_TRACKING_AUTH=descope -# Optional: Where to redirect after login (default is "/") -DESCOPE_REDIRECT_URL=/ +# Optional (with defaults) +export DESCOPE_ADMIN_ROLES="admin,mlflow-admin" +export DESCOPE_DEFAULT_PERMISSION="READ" +export DESCOPE_USERNAME_CLAIM="sub" # or "email" ``` -## Step 5: Run MLflow +## Step 5: Use MLflow -```bash -mlflow server --app-name descope-auth --host 0.0.0.0 --port 5000 -``` +Once configured, the plugin works automatically with any MLflow client: + +```python +import mlflow -## Step 6: Test Authentication +# Set tracking URI to your MLflow server +mlflow.set_tracking_uri("http://localhost:5000") -1. Open your browser and go to `http://localhost:5000` -2. You'll be redirected to the Descope login page -3. Authenticate using one of your configured methods -4. You'll be redirected back to MLflow and logged in! +# Start a run - authentication and user context are automatic! +with mlflow.start_run(): + mlflow.log_param("alpha", 0.5) + mlflow.log_metric("rmse", 0.8) +``` + +The plugin automatically: +- Adds authentication headers to all requests +- Injects user context headers (X-Descope-User-ID, X-Descope-Email, etc.) +- Tags runs with user information (descope.user_id, descope.email, etc.) ## What's Next? +### Verify Plugin is Loaded + +```bash +# Check that the plugin is registered +python -c " +from importlib.metadata import entry_points +eps = entry_points(group='mlflow.request_auth_provider') +print([ep.name for ep in eps]) +" +# Should include 'descope' +``` + ### Configure Admin Access By default, all authenticated users have READ access. To grant admin privileges: -1. In your `.env` file, add: +1. Set admin roles in your environment: ```bash - DESCOPE_ADMIN_ROLES=admin,mlflow-admin + export DESCOPE_ADMIN_ROLES="admin,mlflow-admin" ``` 2. In Descope Console, go to **Authorization → Roles** 3. Create a role called `admin` or `mlflow-admin` 4. Assign this role to specific users -### Add More Authentication Methods - -1. Go to Descope Console → **Authentication → Flows** -2. Edit your flow and add more methods (OAuth, SAML, etc.) -3. **No code changes needed!** Just publish the flow -4. Restart MLflow to see the changes - ### Configure Permissions -Create custom permission mapping in your `.env`: - ```bash # Default permission for all users (READ, EDIT, or MANAGE) -DESCOPE_DEFAULT_PERMISSION=READ +export DESCOPE_DEFAULT_PERMISSION="READ" # Roles that get admin access (MANAGE permission) -DESCOPE_ADMIN_ROLES=admin,mlflow-admin,superuser +export DESCOPE_ADMIN_ROLES="admin,mlflow-admin,superuser" ``` -### Production Deployment +### Automatic Run Tagging -For production, use a proper database: +The plugin automatically adds these tags to all runs: -```bash -mlflow server \ - --app-name descope-auth \ - --backend-store-uri postgresql://user:pass@localhost/mlflow \ - --default-artifact-root s3://my-bucket/mlflow \ - --host 0.0.0.0 \ - --port 5000 -``` +- `descope.user_id` - User's Descope ID +- `descope.username` - Username +- `descope.email` - User's email +- `descope.name` - User's display name +- `descope.roles` - Comma-separated list of roles +- `descope.permissions` - Comma-separated list of permissions +- `descope.tenants` - Comma-separated list of tenants -### Docker Deployment - -Create `docker-compose.yml`: - -```yaml -version: "3.8" - -services: - mlflow: - image: python:3.11-slim - command: > - sh -c " - pip install mlflow mlflow-descope-auth && - mlflow server --app-name descope-auth --host 0.0.0.0 - " - environment: - - DESCOPE_PROJECT_ID=${DESCOPE_PROJECT_ID} - - DESCOPE_FLOW_ID=sign-up-or-in - ports: - - "5000:5000" - volumes: - - mlflow-data:/mlflow - -volumes: - mlflow-data: -``` +### Request Headers -Run with: +The plugin adds these headers to MLflow API requests: -```bash -DESCOPE_PROJECT_ID=P2XXXXX docker-compose up -``` +- `X-Descope-User-ID` +- `X-Descope-Username` +- `X-Descope-Email` +- `X-Descope-Roles` +- `X-Descope-Permissions` +- `X-Descope-Tenants` ## Troubleshooting -### Login page shows but authentication fails - -**Problem**: Web component loads but authentication doesn't work - -**Solution**: - -1. Check that your `DESCOPE_PROJECT_ID` is correct -2. Ensure your flow exists in Descope Console -3. Check browser console for errors -4. Verify your domain is whitelisted in Descope Console → **Project Settings → Authentication Hosts** +### Plugin not loaded -### Redirected to login in a loop - -**Problem**: After successful authentication, you're redirected back to login +**Problem**: Authentication doesn't work **Solution**: -1. Clear browser cookies -2. Check that cookies are enabled -3. If using HTTPS, ensure your redirect URL uses HTTPS too -4. Check browser console for cookie errors +```bash +# Verify plugin is installed +pip list | grep mlflow-descope-auth + +# Check entry points +python -c " +from importlib.metadata import entry_points +eps = entry_points(group='mlflow.request_auth_provider') +print([ep.name for ep in eps]) +" + +# Ensure MLFLOW_TRACKING_AUTH is set +echo $MLFLOW_TRACKING_AUTH # Should be "descope" +``` -### "DESCOPE_PROJECT_ID is required" error +### Authentication fails -**Problem**: MLflow fails to start +**Problem**: Requests fail with authentication errors **Solution**: ```bash -# Make sure .env file exists in the same directory -cat .env - -# Or set environment variable directly -export DESCOPE_PROJECT_ID=P2XXXXX -mlflow server --app-name descope-auth +# Check environment variables +env | grep DESCOPE + +# Verify tokens are valid +python -c " +from mlflow_descope_auth import get_descope_client +import os +client = get_descope_client() +result = client.validate_session(os.environ['DESCOPE_SESSION_TOKEN']) +print('✓ Token valid') +" ``` -### Port 5000 already in use +### "DESCOPE_PROJECT_ID is required" error -**Problem**: `Address already in use` error +**Problem**: Plugin fails to initialize **Solution**: ```bash -# Use a different port -mlflow server --app-name descope-auth --port 5001 - -# Or kill the process using port 5000 -lsof -ti:5000 | xargs kill -9 +# Set the required environment variable +export DESCOPE_PROJECT_ID=P2XXXXX +export DESCOPE_SESSION_TOKEN="" ``` ## Need Help? - **Documentation**: See [README.md](../README.md) for full documentation +- **Architecture**: See [ARCHITECTURE.md](../ARCHITECTURE.md) for technical details - **Issues**: [GitHub Issues](https://github.com/descope/mlflow-descope-auth/issues) - **Descope Docs**: [docs.descope.com](https://docs.descope.com/) ## Next Steps -- Read the [Configuration Guide](../README.md#configuration-reference) -- Learn about [Permission Mapping](../README.md#permission-mapping) -- Explore [Advanced Features](../README.md#advanced-features) -- Check out [Docker Examples](../examples/) +- Read the [Configuration Reference](../README.md#configuration-reference) +- Learn about the [Plugin Architecture](../ARCHITECTURE.md) +- Check out the [Docker Examples](../examples/) Happy MLflow-ing with Descope! 🚀