feat: add generic short code authentication system#3078
feat: add generic short code authentication system#3078apophisnow wants to merge 8 commits intomusic-assistant:devfrom
Conversation
Add a reusable short code authentication system that any provider can use
for QR code login, device pairing, or similar flows.
Changes:
- Add join_codes database table (schema v6)
- Add generate_join_code(user_id, provider_name, ...) method
- Add exchange_join_code() to convert codes to JWT tokens
- Add auth/code public API endpoint
- Add revoke_join_codes() for cleanup
- Update login.html to handle ?join= parameter
- Add provider_name parameter to JWT token encoding
Providers can implement short code auth flows like:
code, expires = await auth.generate_join_code(
user_id=my_user.user_id,
provider_name="my_provider",
expires_in_hours=24,
)
The provider_name is stored in the join code and passed to the JWT token,
allowing providers to identify their authenticated sessions.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Could you please update the auth tests to also include these new short code cases? |
Add tests covering the short code authentication system: - generate_join_code: basic functionality, invalid user handling - exchange_join_code: success, case-insensitivity, expired codes, max_uses limits, unlimited uses, provider_name in JWT claims - revoke_join_codes: per-user revocation, revoke all codes - authenticate_with_join_code API: success and error cases Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added tests. |
There was a problem hiding this comment.
Pull request overview
Adds a reusable “join code” (short code) authentication flow that can be used by providers for QR-code login/device pairing: generates short codes tied to users, exchanges them for JWTs (including provider_name claim), exposes a public auth/code API command, and updates the login page to accept ?join=.
Changes:
- Add
join_codestable + DB schema bump/migration and supporting CRUD inAuthenticationManager. - Add join-code exchange API command (
auth/code) and JWT claim support (provider_name). - Update
login.htmlto auto-authenticate when ajoinquery parameter is present; add tests for the new flow.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
music_assistant/controllers/webserver/auth.py |
Implements join code generation/exchange/revocation, DB schema v5 migration, and public auth/code command. |
music_assistant/helpers/jwt_auth.py |
Adds optional provider_name claim to JWT encoding. |
music_assistant/helpers/resources/login.html |
Adds client-side join-code handling via JSON-RPC call to auth/code. |
tests/test_webserver_auth.py |
Adds test coverage for join code generation, exchange, expiry, max uses, revocation, and API command behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| async def generate_join_code( | ||
| self, | ||
| user_id: str, | ||
| expires_in_hours: int = JOIN_CODE_DEFAULT_EXPIRY_HOURS, | ||
| max_uses: int = 0, | ||
| device_name: str = "Short Code Login", | ||
| provider_name: str | None = None, | ||
| ) -> tuple[str, datetime]: |
There was a problem hiding this comment.
generate_join_code accepts expires_in_hours and max_uses without validation. Negative values currently create already-expired codes or effectively unlimited-use codes (when max_uses is negative). Add input validation to reject negatives (and potentially enforce reasonable upper bounds) so providers can’t accidentally create insecure/invalid codes.
| # Check use limit | ||
| max_uses = code_row["max_uses"] | ||
| use_count = code_row["use_count"] | ||
| if max_uses > 0 and use_count >= max_uses: | ||
| return None |
There was a problem hiding this comment.
When max_uses is exceeded, exchange_join_code returns None but leaves the exhausted code row in join_codes. This can cause unbounded table growth and keeps “dead” codes around indefinitely. Consider deleting the row once it reaches the use limit (and/or adding periodic cleanup of expired/exhausted rows).
| # Generate short alphanumeric code | ||
| code = "".join(secrets.choice(JOIN_CODE_CHARSET) for _ in range(JOIN_CODE_LENGTH)) | ||
| code_hash = hashlib.sha256(code.encode()).hexdigest() | ||
|
|
||
| now = utc() | ||
| expires_at = now + timedelta(hours=expires_in_hours) | ||
|
|
||
| code_data = { | ||
| "code_id": secrets.token_urlsafe(32), | ||
| "code_hash": code_hash, | ||
| "user_id": user_id, | ||
| "created_at": now.isoformat(), | ||
| "expires_at": expires_at.isoformat(), | ||
| "max_uses": max_uses, | ||
| "use_count": 0, | ||
| "device_name": device_name, | ||
| "provider_name": provider_name, | ||
| } | ||
| await self.database.insert("join_codes", code_data) | ||
| await self.database.commit() |
There was a problem hiding this comment.
generate_join_code inserts code_hash with a UNIQUE constraint but does not handle collisions. With 6-char codes, a rare collision will raise an IntegrityError and fail the flow. Consider retrying code generation on UNIQUE-constraint failure (bounded attempts) before giving up with a clear error.
|
Can you please walk me through the entire flow here you envision for the guest role + party mode ? So, you create a user with GUEST role and then you can optionally request a short code token ? At least, I think we should limit this feature to guest accounts only, for the sake of security. Security enhancements:
API improvements:
|
Sure thing. This specific PR is just to provide a mechanism for short join URLs which allow access without requiring manual login. So a URL like http://localhost:8095/?join=6Y6XQC can be provided to a user (through a text message, QR code, or any other means) which will allow them access without creating an account and logging in. For party mode the idea is: Since the join code is short it makes the QR code easier to scan, and if needed the URL could be easily shared with guests by writing it down, through text message, or even reading it aloud. I think your suggestions make sense, i'll work to implement them. |
|
Marked it as draft - please hit the "ready for review" button once you have implemented the suggestions. Thanks! |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Add a reusable short code authentication system that any provider can use for QR code login, device pairing, or similar flows.
Changes:
Providers can implement short code auth flows like:
The provider_name is stored in the join code and passed to the JWT token, allowing providers to identify their authenticated sessions.