|
| 1 | +export const configExample = { |
| 2 | + domain: 'client.example.com', |
| 3 | + redirect_urls: ['https://client.example.com/oauth/callback'], |
| 4 | + enabled_auth_methods: ['email_password', 'google'], |
| 5 | + ui_theme: { |
| 6 | + colors: { |
| 7 | + bg: '#f8fafc', |
| 8 | + surface: '#ffffff', |
| 9 | + text: '#0f172a', |
| 10 | + muted: '#475569', |
| 11 | + primary: '#2563eb', |
| 12 | + primary_text: '#ffffff', |
| 13 | + border: '#e2e8f0', |
| 14 | + danger: '#dc2626', |
| 15 | + danger_text: '#ffffff', |
| 16 | + }, |
| 17 | + radii: { |
| 18 | + card: '16px', |
| 19 | + button: '12px', |
| 20 | + input: '12px', |
| 21 | + }, |
| 22 | + density: 'comfortable', |
| 23 | + typography: { |
| 24 | + font_family: 'sans', |
| 25 | + base_text_size: 'md', |
| 26 | + }, |
| 27 | + button: { |
| 28 | + style: 'solid', |
| 29 | + }, |
| 30 | + card: { |
| 31 | + style: 'bordered', |
| 32 | + }, |
| 33 | + logo: { |
| 34 | + url: '', |
| 35 | + alt: 'Client logo', |
| 36 | + text: 'Client', |
| 37 | + font_size: '24px', |
| 38 | + color: '#0f172a', |
| 39 | + style: { |
| 40 | + 'font-weight': '800', |
| 41 | + 'letter-spacing': '-0.02em', |
| 42 | + }, |
| 43 | + }, |
| 44 | + }, |
| 45 | + language_config: 'en', |
| 46 | + debug_enabled: true, |
| 47 | + allow_registration: true, |
| 48 | + registration_mode: 'password_required', |
| 49 | + user_scope: 'global', |
| 50 | +}; |
| 51 | + |
| 52 | +export const configJwtDocumentation = { |
| 53 | + description: |
| 54 | + 'The config JWT is a signed JWT containing all client-specific settings. The payload is the config. The signature must be created with the shared secret and the JWT aud must match AUTH_SERVICE_IDENTIFIER.', |
| 55 | + signing: { |
| 56 | + algorithms: ['HS256', 'HS384', 'HS512'], |
| 57 | + audience: |
| 58 | + 'The JWT aud claim must match the auth service identifier configured in AUTH_SERVICE_IDENTIFIER.', |
| 59 | + }, |
| 60 | + required_fields: { |
| 61 | + domain: |
| 62 | + 'string — client domain. This must exactly match the hostname of config_url when the auth service fetches the JWT.', |
| 63 | + redirect_urls: |
| 64 | + 'string[] — non-empty list of absolute HTTP/HTTPS callback URLs. redirect_url matching is exact.', |
| 65 | + enabled_auth_methods: |
| 66 | + 'string[] — non-empty list. Supported values are email_password, google, facebook, github, linkedin, apple.', |
| 67 | + language_config: |
| 68 | + 'string | string[] — either one language code or a non-empty array of language codes.', |
| 69 | + ui_theme: { |
| 70 | + description: |
| 71 | + 'object — every auth page style comes from ui_theme. This object is required and the main source of integration mistakes.', |
| 72 | + required_sections: { |
| 73 | + colors: { |
| 74 | + required_keys: [ |
| 75 | + 'bg', |
| 76 | + 'surface', |
| 77 | + 'text', |
| 78 | + 'muted', |
| 79 | + 'primary', |
| 80 | + 'primary_text', |
| 81 | + 'border', |
| 82 | + 'danger', |
| 83 | + 'danger_text', |
| 84 | + ], |
| 85 | + value_format: |
| 86 | + 'hex color only (#RGB, #RGBA, #RRGGBB, #RRGGBBAA) or transparent', |
| 87 | + }, |
| 88 | + radii: { |
| 89 | + required_keys: ['card', 'button', 'input'], |
| 90 | + value_format: 'CSS length string: px, rem, em, %, or 0', |
| 91 | + }, |
| 92 | + density: 'compact | comfortable | spacious', |
| 93 | + typography: { |
| 94 | + required_keys: ['font_family', 'base_text_size'], |
| 95 | + font_family: |
| 96 | + 'Preset values like sans, serif, mono are valid. Custom CSS font-family names are also valid.', |
| 97 | + base_text_size: 'sm | md | lg', |
| 98 | + font_import_url: |
| 99 | + 'optional HTTP/HTTPS stylesheet URL. Use this when font_family depends on a remote font import.', |
| 100 | + }, |
| 101 | + button: { |
| 102 | + required_keys: ['style'], |
| 103 | + style: 'solid | outline | ghost', |
| 104 | + }, |
| 105 | + card: { |
| 106 | + required_keys: ['style'], |
| 107 | + style: 'plain | bordered | shadow', |
| 108 | + }, |
| 109 | + logo: { |
| 110 | + required_keys: ['url', 'alt'], |
| 111 | + url: 'HTTP/HTTPS URL or empty string', |
| 112 | + alt: 'required non-empty string', |
| 113 | + text: 'optional text logo, max 100 chars, used when url is empty', |
| 114 | + font_size: 'optional CSS length string', |
| 115 | + color: 'optional hex color', |
| 116 | + style: |
| 117 | + 'optional flat object of CSS property -> safe CSS value. Do not include semicolons, braces, url(), or expression().', |
| 118 | + }, |
| 119 | + }, |
| 120 | + optional_sections: { |
| 121 | + css_vars: |
| 122 | + 'Record<string, string> — optional advanced CSS variable overrides.', |
| 123 | + }, |
| 124 | + common_failures: [ |
| 125 | + 'Missing one of the required ui_theme sections such as colors, radii, button, card, typography, or logo.', |
| 126 | + 'Using non-hex colors like rgb(...), hsl(...), or named colors.', |
| 127 | + 'Using bare numbers for radii/font sizes instead of CSS length strings like 12px or 1rem.', |
| 128 | + 'Providing a text logo without logo.alt.', |
| 129 | + 'Forgetting button.style or card.style.', |
| 130 | + ], |
| 131 | + example: configExample.ui_theme, |
| 132 | + }, |
| 133 | + }, |
| 134 | + optional_fields: { |
| 135 | + '2fa_enabled': 'boolean (default false)', |
| 136 | + debug_enabled: 'boolean (default false)', |
| 137 | + allowed_social_providers: 'string[] — subset of enabled social providers', |
| 138 | + user_scope: '"global" | "per_domain" (default "global")', |
| 139 | + allow_registration: 'boolean (default true)', |
| 140 | + registration_mode: |
| 141 | + '"password_required" | "passwordless" (default "password_required")', |
| 142 | + allowed_registration_domains: |
| 143 | + 'string[] — lowercase email domains allowed to register', |
| 144 | + registration_domain_mapping: |
| 145 | + 'array of { email_domain, org_id, team_id? } — email-domain-based org/team placement', |
| 146 | + language: 'string — currently selected language override', |
| 147 | + session: { |
| 148 | + remember_me_enabled: 'boolean (default true)', |
| 149 | + remember_me_default: 'boolean (default true)', |
| 150 | + short_refresh_token_ttl_hours: 'number (1-168, default 1)', |
| 151 | + long_refresh_token_ttl_days: 'number (1-90, default 30)', |
| 152 | + access_token_ttl_minutes: 'number (15-60)', |
| 153 | + }, |
| 154 | + org_features: { |
| 155 | + enabled: 'boolean (default false)', |
| 156 | + groups_enabled: 'boolean (default false)', |
| 157 | + user_needs_team: 'boolean (default false)', |
| 158 | + max_teams_per_org: 'number (default 100, max 1000)', |
| 159 | + max_groups_per_org: 'number (default 20, max 200)', |
| 160 | + max_members_per_org: 'number (default 1000, max 10000)', |
| 161 | + max_members_per_team: 'number (default 200, max 5000)', |
| 162 | + max_members_per_group: 'number (default 500, max 5000)', |
| 163 | + max_team_memberships_per_user: 'number (default 50, max 200)', |
| 164 | + org_roles: |
| 165 | + 'string[] (default ["owner", "admin", "member"]). Must include "owner".', |
| 166 | + }, |
| 167 | + access_requests: { |
| 168 | + enabled: 'boolean (default false)', |
| 169 | + target_org_id: 'string (required when enabled=true)', |
| 170 | + target_team_id: 'string (required when enabled=true)', |
| 171 | + auto_grant_domains: 'string[]', |
| 172 | + notify_org_roles: 'string[] (default ["owner", "admin"])', |
| 173 | + admin_review_url: 'absolute URL', |
| 174 | + }, |
| 175 | + }, |
| 176 | + example_payload: configExample, |
| 177 | +}; |
| 178 | + |
| 179 | +export const configVerificationEndpointDocumentation = { |
| 180 | + path: '/config/verify', |
| 181 | + method: 'POST', |
| 182 | + description: |
| 183 | + 'Debug endpoint that validates raw config JSON, a signed config JWT, or a config_url fetch target. It reports schema problems separately from signature, audience, and config_url/domain issues.', |
| 184 | + body: { |
| 185 | + config: |
| 186 | + 'object (optional) — raw config payload to schema-validate directly. This skips JWT signature checking unless config_jwt or config_url is also supplied instead.', |
| 187 | + config_jwt: |
| 188 | + 'string (optional) — signed config JWT to decode, inspect, schema-validate, and optionally verify with shared_secret.', |
| 189 | + config_url: |
| 190 | + 'string (optional) — URL that should return the signed config JWT. The endpoint fetches it and then runs the same checks.', |
| 191 | + shared_secret: |
| 192 | + 'string (optional) — candidate secret used to verify config_jwt or the JWT fetched from config_url. If it is wrong, the response explicitly reports the shared secret/signature check failure.', |
| 193 | + auth_service_identifier: |
| 194 | + 'string (optional) — expected JWT aud. Defaults to this auth service environment when omitted.', |
| 195 | + }, |
| 196 | + source_priority: ['config', 'config_jwt', 'config_url'], |
| 197 | + response: { |
| 198 | + ok: 'boolean — true when every executed check passed', |
| 199 | + schema_valid: 'boolean', |
| 200 | + jwt_signature_valid: |
| 201 | + 'boolean | null — null when signature checking was skipped', |
| 202 | + audience_valid: 'boolean | null — null when no audience check was possible', |
| 203 | + domain_match: 'boolean | null — null when config_url was not part of the request or schema parsing failed', |
| 204 | + checks: |
| 205 | + 'object — per-stage results for source, fetch, decode, signature, audience, schema, and domain_match', |
| 206 | + issues: 'array — structured stage-specific failures and warnings', |
| 207 | + config_summary: |
| 208 | + 'object | null — safe summary of the parsed config when schema validation succeeds', |
| 209 | + }, |
| 210 | +}; |
0 commit comments