diff options
| author | Claude Sonnet 4.6 | 2026-06-03 00:00:00 +0000 |
|---|---|---|
| committer | Frederick Muriuki Muriithi | 2026-06-03 14:40:23 -0500 |
| commit | 7c44220d2a2683c17369dcf4d6b24d8dd2df62ab (patch) | |
| tree | 5fab49155b43973361fd69e60ec965ebacf90c89 | |
| parent | 6a0d09960be96619d26d080d8d4c420eb48adb25 (diff) | |
| download | gn-auth-7c44220d2a2683c17369dcf4d6b24d8dd2df62ab.tar.gz | |
wsgi: add __create_one_client__ helper and create-oauth2-client
Add a __create_one_client__ helper that constructs an OAuth2Client, hashes the secret, persists it via save_client, and returns a credential record dict. Add create-oauth2-client CLI command that exposes all client parameters explicitly. Preparation for reuse by create-test-oauth2-client.
| -rw-r--r-- | gn_auth/wsgi.py | 92 |
1 files changed, 91 insertions, 1 deletions
diff --git a/gn_auth/wsgi.py b/gn_auth/wsgi.py index a6d8b00..0feb69a 100644 --- a/gn_auth/wsgi.py +++ b/gn_auth/wsgi.py @@ -7,7 +7,7 @@ import uuid import json from math import ceil from pathlib import Path -from datetime import datetime +from datetime import datetime, timezone import click from yoyo import get_backend, read_migrations @@ -23,6 +23,9 @@ from gn_auth.auth.authorisation.roles.models import assign_default_roles from gn_auth.auth.authorisation.users.admin.models import ( make_sys_admin, grant_sysadmin_role) from gn_auth.auth.authorisation.users.models import delete_users_by_id +from gn_auth.auth.authentication.oauth2.models.oauth2client import ( + OAuth2Client, save_client, delete_client, + client as oauth2_client_by_id) from gn_auth.scripts import register_sys_admin as rsysadm# type: ignore[import] @@ -297,6 +300,93 @@ def create_test_users(session_timestamp, user_specs, output_path): {"session_timestamp": session_timestamp, "users": records}, output_path) + +_DEFAULT_GRANT_TYPES_ = ( + "password", + "authorization_code", + "refresh_token", + "urn:ietf:params:oauth:grant-type:jwt-bearer", +) + +_DEFAULT_SCOPES_ = ( + "profile", "group", "role", "resource", + "register-client", "user", "masquerade", + "migrate-data", "introspect", +) + + +def __create_one_client__( + conn, + client_name: str, + owner_user, + redirect_uris: tuple, + scopes: tuple = _DEFAULT_SCOPES_, + grant_types: tuple = _DEFAULT_GRANT_TYPES_, + jwks_uri: str = "", +) -> dict: + """Create a single OAuth2 client and return its credential record.""" + raw_secret = secrets.token_urlsafe(32) + the_client = OAuth2Client( + client_id=uuid.uuid4(), + client_secret=hash_password(raw_secret), + client_id_issued_at=datetime.now(tz=timezone.utc), + client_secret_expires_at=datetime.fromtimestamp(0), + client_metadata={ + "client_name": client_name, + "token_endpoint_auth_method": [ + "client_secret_post", "client_secret_basic"], + "client_type": "confidential", + "grant_types": list(grant_types), + "default_redirect_uri": redirect_uris[0] if redirect_uris else "", + "redirect_uris": list(redirect_uris), + "response_type": ["code", "token"], + "scope": list(scopes), + "public-jwks-uri": jwks_uri, + }, + user=owner_user) + save_client(conn, the_client) + return { + "client_id": str(the_client.client_id), + "client_secret": raw_secret, + "client_name": client_name, + } + + +@app.cli.command() +@click.option("--name", "client_name", required=True, + help="Human-readable name for the OAuth2 client") +@click.option("--owner-id", required=True, type=click.UUID, + help="UUID of the user who owns this client") +@click.option("--redirect-uri", "redirect_uris", multiple=True, + help="Allowed redirect URI (repeatable)") +@click.option("--scope", "scopes", multiple=True, + default=_DEFAULT_SCOPES_, show_default=False, + help="OAuth2 scope (repeatable; defaults to full scope set)") +@click.option("--grant-type", "grant_types", multiple=True, + default=_DEFAULT_GRANT_TYPES_, show_default=False, + help="Grant type (repeatable; defaults to all standard types)") +@click.option("--jwks-uri", default="", + help="URI to the client's public JWKS (optional)") +@click.option("--output", "output_path", type=click.Path(), default=None, + help="Write credentials as JSON to this file (default: stdout)") +def create_oauth2_client(client_name, owner_id, redirect_uris, scopes, + grant_types, jwks_uri, output_path): + """Create an OAuth2 client with specified parameters. + + Scopes and grant types default to the full standard set if not provided. + """ + with db.connection(app.config["AUTH_DB"]) as conn: + try: + owner = user_by_id(conn, owner_id) + except NotFoundError: + print(f"No user found with ID {owner_id}", file=sys.stderr) + sys.exit(1) + record = __create_one_client__( + conn, client_name, owner, redirect_uris, scopes, grant_types, + jwks_uri) + + __write_output__({"client": record}, output_path) + ##### END: CLI Commands ##### if __name__ == '__main__': |
