Skip to content

Added OAuth-Based Discord Account Integration#19

Merged
yulmwu merged 5 commits into
mainfrom
feat/discord-integration
Jun 24, 2026
Merged

Added OAuth-Based Discord Account Integration#19
yulmwu merged 5 commits into
mainfrom
feat/discord-integration

Conversation

@yulmwu

@yulmwu yulmwu commented Jun 23, 2026

Copy link
Copy Markdown
Member

This PR introduces Discord account integration using OAuth. Once a user links their Discord account, they are automatically joined to a designated Discord server and assigned a role.

All Discord interactions are handled by a separate service independent of the main backend. The backend is responsible only for managing OAuth clients and storing account-linking information. The overall flow is shown below.

invitebot drawio

The following backend environment variables have been added:

# Discord Account Linking (OAuth2 on the backend; bot runs as a separate server)
DISCORD_ENABLED=true
DISCORD_CLIENT_ID=
DISCORD_CLIENT_SECRET=
# redirect_uri = the URL the BROWSER hits at the backend callback. Register it
# verbatim in the Discord Developer Portal (OAuth2 > Redirects; multiple allowed)
# and keep it identical here (scheme/host/port/path all matter).
#   - local direct:  http://localhost:8080/api/discord/callback
#   - production:    https://internal.swua.kr/wargame/api/discord/callback
DISCORD_REDIRECT_URI=http://localhost:8080/api/discord/callback
DISCORD_OAUTH_SCOPES=identify guilds.join
DISCORD_STATE_TTL=5m
DISCORD_OAUTH_TIMEOUT=10s
# Where to send the browser after the OAuth callback (frontend profile page).
#   - production: https://wargame.swua.kr/profile
DISCORD_SUCCESS_REDIRECT=http://localhost:3000/profile
# Invite link shown to users not yet in the guild (fallback button).
DISCORD_INVITE_URL=
# Auto-join the guild via guilds.join scope after linking.
DISCORD_AUTO_JOIN=true
# Connection to the separate Discord bot server.
DISCORD_BOT_BASE_URL=http://localhost:8083
DISCORD_BOT_SECRET=change-me
DISCORD_BOT_TIMEOUT=5s

DISCORD_CLIENT_ID and DISCORD_CLIENT_SECRET can be obtained from the Discord Developer Portal. You must also configure the corresponding Redirect URI.

In addition, DISCORD_BOT_SECRET must match the value used by the Invite Bot/Server. The following environment variables have been added for the Invite Bot/Server:

# Discord bot server configuration.
# The bot owns all Discord secrets; the wargame backend never sees these.

# HTTP server (internal API consumed by the wargame backend)
HTTP_ADDR=:8083

# Shared secret — MUST equal DISCORD_BOT_SECRET in the wargame backend .env
DISCORD_INTERNAL_SECRET=change-me

# Discord application / bot
DISCORD_BOT_TOKEN=
DISCORD_GUILD_ID=
DISCORD_VERIFIED_ROLE_ID=

# Optional
DISCORD_AUDIT_REASON=External site verification

Likewise, the bot token, guild ID, and role ID should be obtained from the Discord Developer Portal and configured appropriately for your environment.

Warning

The backend server and the Invite Bot/Server are intentionally separated due to bot sharding concerns. The current implementation was not designed with a general distributed environment in mind, so the bot server should be deployed as a separate single instance. This architecture may be improved in the future.

For more details, please refer to the updated source code and documentation.

@yulmwu yulmwu added the enhancement New feature or request label Jun 23, 2026
Comment thread invite-bot/src/index.ts

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will consider managing the Invite Bot/Server source code separately as a subtree, since the same source code is also used in the SMCTF project.

@codecov

codecov Bot commented Jun 23, 2026

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds end-to-end Discord account linking via OAuth2, persisting link state in the backend and delegating guild/role actions to a separate invite-bot service, with frontend UI to connect/sync/unlink.

Changes:

  • Backend: Discord OAuth flow + persistence (discord_connections) + new /api/discord/* endpoints.
  • New standalone invite-bot Node/TypeScript service exposing an internal HTTP API to join/kick/grant role.
  • Frontend: profile “Discord” card + i18n strings + API bindings; plus docs and compose wiring.

Reviewed changes

Copilot reviewed 51 out of 53 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
migrations/2026-06-23/001_add_discord_connections.sql Adds discord_connections table + unique indexes.
migrations/2026-06-23/999_rollback.sql Rollback drops discord_connections.
invite-bot/tsconfig.json TypeScript build configuration for invite-bot service.
invite-bot/src/logger.ts Minimal JSON logger for invite-bot.
invite-bot/src/index.ts Invite-bot entrypoint: start Discord client + HTTP server + shutdown handling.
invite-bot/src/http/server.ts Internal HTTP API (auth + join/kick/grant/status endpoints).
invite-bot/src/discord/errors.ts Maps Discord API errors to internal bot error codes/status.
invite-bot/src/discord/client.ts Discord.js client wrapper implementing join/kick/grant/status.
invite-bot/src/config.ts Reads env config for invite-bot.
invite-bot/README.md Documents internal API routes and local commands.
invite-bot/package.json Invite-bot dependencies/scripts/engine constraints.
invite-bot/package-lock.json Locked dependency tree for invite-bot.
invite-bot/Dockerfile Multi-stage Docker build for invite-bot runtime.
invite-bot/.prettierrc Prettier formatting configuration for invite-bot.
invite-bot/.prettierignore Files excluded from formatting.
invite-bot/.gitignore Git ignores for invite-bot artifacts.
invite-bot/.env.example Example env file for invite-bot configuration.
internal/service/errors.go Adds Discord-related service error constants.
internal/service/discord_service.go Implements OAuth state, callback handling, provisioning, unlink/sync/status.
internal/service/discord_service_test.go Unit tests for DiscordService linking/sync/unlink/state validation.
internal/repo/testenv_test.go Ensures repo tests truncate discord_connections.
internal/repo/discord_repo.go Repo CRUD for discord_connections.
internal/repo/discord_repo_test.go Repo tests for CRUD + unique constraint behavior.
internal/models/discord_connection.go Bun model + status constants for Discord connection row.
internal/http/router.go Wires new Discord routes and injects DiscordService into handlers.
internal/http/integration/testenv_test.go Updates router creation in integration tests for new param.
internal/http/handlers/testenv_test.go Updates handler construction for new DiscordService param.
internal/http/handlers/handler.go Adds DiscordService field to Handler and updates constructor.
internal/http/handlers/handler_test.go Updates handler construction for new DiscordService param.
internal/http/handlers/errors.go Maps Discord service errors to HTTP statuses.
internal/http/handlers/discord.go Adds connect/callback/status/sync-role/unlink handlers + redirect result mapping.
internal/http/handlers/discord_test.go Handler tests for redirects, status, sync-role, unlink, disabled behavior.
internal/discord/oauth.go OAuth client for Discord authorize URL, token exchange, and /users/@me.
internal/discord/oauth_test.go OAuth client tests (params, form encoding, bearer header, error status).
internal/discord/client.go Backend HTTP client to invite-bot internal API + error mapping.
internal/discord/client_test.go Bot client tests for request paths, auth, payloads, and error mapping.
internal/db/db.go Adds DiscordConnection to auto-migrate + index creation.
internal/config/config.go Adds Discord config fields, env parsing, validation, and redaction.
frontend/src/routes/UserProfile.tsx Adds DiscordLinkCard to profile page.
frontend/src/routes/ChallengeDetail.tsx Formatting-only JSX changes.
frontend/src/routes/admin/Popups.tsx Formatting-only JSX changes.
frontend/src/locales/ko.json Adds Discord profile strings (Korean) + trailing comma fix.
frontend/src/locales/ja.json Adds Discord profile strings (Japanese) + trailing comma fix.
frontend/src/locales/en.json Adds Discord profile strings (English) + trailing comma fix.
frontend/src/lib/types.ts Adds DiscordStatus + DiscordRoleStatus types.
frontend/src/lib/api.ts Adds Discord API helpers (connect URL, status, sync-role, unlink).
frontend/src/components/UserProfile/DiscordLinkCard.tsx UI for connect/sync/unlink + banner messaging and avatar rendering.
docs/docs/discord.md Detailed backend flow/API documentation and env configuration docs.
docker-compose.yaml Adds invite-bot service and points backend to it.
codecov.yaml Excludes invite-bot from Go coverage reporting.
cmd/server/main.go Instantiates Discord service clients and injects DiscordService into router.
.env.example Documents new Discord-related backend env vars.
Files not reviewed (1)
  • invite-bot/package-lock.json: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/config/config.go
Comment thread internal/service/discord_service.go
Comment thread internal/service/discord_service.go
Comment thread invite-bot/package.json Outdated
Comment thread invite-bot/src/config.ts
@yulmwu

yulmwu commented Jun 24, 2026

Copy link
Copy Markdown
Member Author

Okay. I think we can merge now.

@yulmwu yulmwu merged commit 97bd609 into main Jun 24, 2026
2 checks passed
@yulmwu yulmwu deleted the feat/discord-integration branch June 24, 2026 01:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants