{"id":9825,"library":"identity","title":"Identity","description":"Identity is an authentication and authorization library optimized for web applications, building upon Microsoft's MSAL Python. It provides high-level APIs for popular frameworks like Flask, Quart, and Django, simplifying integration with Microsoft Identity Platform. The library is actively maintained with frequent updates addressing bug fixes and introducing new features.","status":"active","version":"0.11.0","language":"en","source_language":"en","source_url":"https://github.com/rayluo/identity","tags":["authentication","authorization","oauth","microsoft identity","flask","django","quart","msal"],"install":[{"cmd":"pip install identity[flask]","lang":"bash","label":"For Flask integration"},{"cmd":"pip install identity[django]","lang":"bash","label":"For Django integration"},{"cmd":"pip install identity[quart]","lang":"bash","label":"For Quart integration"},{"cmd":"pip install identity","lang":"bash","label":"Core library only"}],"dependencies":[{"reason":"Core authentication logic is built on MSAL Python.","package":"msal","optional":false},{"reason":"Required for Flask integration features.","package":"flask","optional":true},{"reason":"Required for Django integration features.","package":"django","optional":true},{"reason":"Required for Quart integration features.","package":"quart","optional":true}],"imports":[{"note":"For high-level framework integration (Flask, Django, Quart), use the framework-specific import. `identity.web.Auth` is for lower-level or custom scenarios.","wrong":"from identity.web import Auth","symbol":"Auth","correct":"from identity.flask import Auth"},{"note":"Similar to `Auth`, prefer the framework-specific `login_required` decorator for seamless integration.","wrong":"from identity.web import login_required","symbol":"login_required","correct":"from identity.flask import login_required"}],"quickstart":{"code":"import os\nfrom flask import Flask, render_template_string, session, redirect, url_for, request\nfrom identity.flask import Auth, login_required\n\napp = Flask(__name__)\napp.secret_key = os.urandom(32) # Use a strong, rotated key in production\n\n# Configure Identity using environment variables\nauth = Auth(\n    app,\n    authority=os.environ.get('IDENTITY_AUTHORITY', 'https://login.microsoftonline.com/common'),\n    client_id=os.environ.get('IDENTITY_CLIENT_ID', 'YOUR_CLIENT_ID'),\n    client_secret=os.environ.get('IDENTITY_CLIENT_SECRET', 'YOUR_CLIENT_SECRET'),\n    redirect_uri=os.environ.get('IDENTITY_REDIRECT_URI', 'http://localhost:5000/redirect'),\n    endpoint=os.environ.get('IDENTITY_ENDPOINT', 'https://graph.microsoft.com/v1.0/users'),\n    scope=os.environ.get('IDENTITY_SCOPE', 'User.ReadBasic.All').split()\n)\n\n@app.route(\"/\")\n@login_required\ndef index():\n    user_data = session.get('user', {})\n    return render_template_string(\n        \"\"\"\n        <h1>Welcome, {{ user.get('name', 'Guest') }}!</h1>\n        <p>Logged in user details: {{ user }}</p>\n        <p><a href=\"{{ url_for('logout') }}\">Logout</a></p>\n        \"\"\",\n        user=user_data\n    )\n\n@app.route(auth.redirect_uri_path)\ndef auth_redirect():\n    auth.complete_login(request.args)\n    return redirect(url_for(\"index\"))\n\n@app.route(\"/logout\")\ndef logout():\n    return auth.logout(url_for(\"index\", _external=True))\n\nif __name__ == \"__main__\":\n    # Set dummy values for quick local test if env vars are not set\n    os.environ.setdefault('IDENTITY_CLIENT_ID', 'YOUR_CLIENT_ID_FROM_AZURE')\n    os.environ.setdefault('IDENTITY_CLIENT_SECRET', 'YOUR_CLIENT_SECRET_FROM_AZURE')\n    # Make sure to replace YOUR_TENANT_ID with your actual tenant ID or 'common' for multi-tenant\n    os.environ.setdefault('IDENTITY_AUTHORITY', 'https://login.microsoftonline.com/YOUR_TENANT_ID')\n    os.environ.setdefault('IDENTITY_REDIRECT_URI', 'http://localhost:5000/redirect')\n    os.environ.setdefault('IDENTITY_ENDPOINT', 'https://graph.microsoft.com/v1.0/me')\n    os.environ.setdefault('IDENTITY_SCOPE', 'User.ReadBasic.All')\n\n    print(\"\\n--- To run this app, make sure to replace placeholders YOUR_CLIENT_ID_FROM_AZURE and YOUR_CLIENT_SECRET_FROM_AZURE with actual values from your Azure App Registration. ---\\n\")\n    app.run(debug=True, port=5000)\n","lang":"python","description":"This Flask example demonstrates how to set up `identity` for authentication. It initializes `Auth` with credentials typically pulled from environment variables, protects the root route with `@login_required`, handles the redirect after login, and provides a logout mechanism. Ensure you replace the placeholder environment variables with your actual Azure AD (or Entra ID) application registration details for client ID, client secret, and authority."},"warnings":[{"fix":"If you require the old behavior (always show a login page), explicitly set `prompt=\"select_account\"` in the `Auth` constructor: `Auth(..., prompt=\"select_account\")`.","message":"The default behavior of the `prompt` parameter in `Auth` has changed. Previously, it effectively always prompted for login; now, it defaults to `None` which allows automatic sign-in if an active session with the identity provider already exists.","severity":"breaking","affected_versions":"0.11.0+"},{"fix":"Update your Django view functions to include the `context` parameter: `def your_view(request, *, context):`.","message":"Django views decorated by `@login_required` must now accept a keyword-only parameter named `context`.","severity":"breaking","affected_versions":"0.6.0+"},{"fix":"Update your Django templates to use the new logout URL pattern: `<a href=\"{% url 'identity.logout' %}\">Logout</a>`.","message":"The Django API's logout URL was normalized, potentially breaking existing logout links in templates.","severity":"breaking","affected_versions":"0.7.0+"},{"fix":"Instead of `auth = Auth(app, ...)`, initialize with `auth = Auth(...)` and then `auth.init_app(app)`.","message":"When using Flask/Quart factory patterns, `Auth` must be initialized using `init_app(app)` after the application object is created.","severity":"gotcha","affected_versions":"0.9.0+"}],"env_vars":null,"last_verified":"2026-04-17T00:00:00.000Z","next_check":"2026-07-16T00:00:00.000Z","problems":[{"fix":"Modify your Django view function signature to accept `context`: `def your_view_func(request, *, context):`","cause":"After upgrading to `identity` 0.6.0 or later, Django views decorated with `login_required` now expect a `context` keyword argument.","error":"TypeError: your_view_func() missing 1 required keyword-only argument: 'context'"},{"fix":"Ensure that the `Auth` constructor includes a valid `redirect_uri` that matches what's configured in your Identity Provider (e.g., Azure AD App Registration): `auth = Auth(..., redirect_uri=\"http://localhost:5000/redirect\")`","cause":"The `redirect_uri` parameter was either omitted or incorrectly configured in the `Auth` constructor, which is crucial for the OAuth flow.","error":"RuntimeError: redirect_uri is not configured. Please provide it in Auth(redirect_uri=...)"},{"fix":"If you explicitly want to force a login prompt every time (the old behavior), set `prompt=\"select_account\"` in your `Auth` configuration: `auth = Auth(..., prompt=\"select_account\")`.","cause":"As of `identity` 0.11.0, the default for the `prompt` parameter in the `Auth` constructor changed, allowing automatic sign-in. If you upgrade from an older version, the behavior might seem like it's always prompting.","error":"Users are always prompted to log in, even if they have an active session with the Identity Provider."}]}