Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Add comprehensive login page customization options #7374

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

strickvl
Copy link

@strickvl strickvl commented Jun 12, 2025

This PR adds comprehensive customization options for the login page, allowing users to customize all text elements through CLI arguments, environment variables, or config files.

🎯 Key Features

  • Complete login customization - Every text element can be customized
  • Multiple configuration methods - CLI args, environment variables, YAML config
  • Docker-friendly - Perfect for containerized deployments with env vars
  • Corporate branding - Customize titles, messages, and error text
  • Security-first - All user input is HTML-escaped to prevent XSS
  • Backwards compatible - Existing --app-name/--welcome-text unchanged

📋 New CLI Options

--login-title                      # Custom login page title
--login-below                      # Custom text below login title  
--password-placeholder             # Custom password field placeholder
--submit-text                      # Custom submit button text
--login-password-msg               # Custom config file password message
--login-env-password-msg           # Custom env password message
--login-hashed-password-msg        # Custom hashed password message
--login-rate-limit-msg             # Custom rate limiting message
--missing-password-msg             # Custom missing password error
--incorrect-password-msg           # Custom incorrect password error

🐳 Environment Variables

All CLI options have corresponding CS_* environment variables:

CS_LOGIN_TITLE, CS_LOGIN_BELOW, CS_PASSWORD_PLACEHOLDER, CS_SUBMIT_TEXT
CS_LOGIN_PASSWORD_MSG, CS_LOGIN_ENV_PASSWORD_MSG, CS_LOGIN_HASHED_PASSWORD_MSG  
CS_LOGIN_RATE_LIMIT_MSG, CS_MISSING_PASSWORD_MSG, CS_INCORRECT_PASSWORD_MSG

💼 Use Cases

Docker deployment with branding:

docker run -e CS_LOGIN_TITLE="ACME Corp Portal" \
           -e CS_LOGIN_ENV_PASSWORD_MSG="Contact IT for access" \
           -e CS_PASSWORD_PLACEHOLDER="Corporate ID" \
           codercom/code-server:latest

Corporate error messages:

code-server --missing-password-msg="Please enter your employee ID" \
           --incorrect-password-msg="Invalid credentials. Contact support."

🔧 Implementation Details

  • Priority order: CLI args → env vars → config file → i18n defaults
  • HTML escaping: All custom messages are automatically escaped for security
  • Internationalization: Non-customized messages still use locale files
  • Config file support: All options work in YAML config files
  • Testing: Comprehensive unit and integration tests added

🧪 Test Plan

  • CLI argument parsing for all new options
  • Environment variable processing
  • Config file YAML support
  • HTML output correctness (titles, placeholders, buttons)
  • HTML escaping security (XSS prevention)
  • Error message customization
  • Backwards compatibility with existing options
  • Internationalization preservation
  • Priority order (CLI > env > config > defaults)

📚 Documentation

  • Updated docs/install.md with Docker customization examples
  • Enhanced docs/FAQ.md with login customization section
  • Added comprehensive docs/customization.md guide
  • Updated main docs/README.md with link to customization guide

This enables powerful branding and customization for enterprise deployments while maintaining full backwards compatibility.

Add CLI arguments and environment variables to customize all login page elements:

- Login title, subtitle, and welcome text
- Password field placeholder and submit button text
- Password instruction messages (config file, env var, hashed)
- Error messages (rate limit, missing/incorrect password)

New CLI options:
  --login-title, --login-below, --password-placeholder, --submit-text
  --login-password-msg, --login-env-password-msg, --login-hashed-password-msg
  --login-rate-limit-msg, --missing-password-msg, --incorrect-password-msg

New environment variables:
  CS_LOGIN_TITLE, CS_LOGIN_BELOW, CS_PASSWORD_PLACEHOLDER, CS_SUBMIT_TEXT
  CS_LOGIN_PASSWORD_MSG, CS_LOGIN_ENV_PASSWORD_MSG, CS_LOGIN_HASHED_PASSWORD_MSG
  CS_LOGIN_RATE_LIMIT_MSG, CS_MISSING_PASSWORD_MSG, CS_INCORRECT_PASSWORD_MSG

Features:
- Full backwards compatibility with existing --app-name/--welcome-text
- HTML escaping for security (prevents XSS)
- Config file support (YAML)
- Priority: CLI args > env vars > config file > defaults
- Internationalization preserved for non-customized messages

Perfect for Docker deployments and corporate branding.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
@strickvl strickvl requested a review from a team as a code owner June 12, 2025 17:19
@code-asher
Copy link
Member

code-asher commented Jun 13, 2025

Thank you for the contribution!

Thinking out loud, my first impression is that the flags are not scaling well. Since we have this concept of language files that we can use for strings in the app, I wonder if we can re-use this for user-provided strings as well? Maybe we have a flag that accepts a json file (or accepts raw json itself) and then we can pass that to the i18n library (merged with the default language file).

Then, all strings, including future ones, will automatically be available for configuration. What do you think?

We could then also deprecate --welcome-msg and --app-name.

@strickvl
Copy link
Author

Sounds good. I'll work on that change.

strickvl and others added 5 commits June 17, 2025 12:53
Replace non-scalable individual flags (--login-title, --login-below, etc.)
with a unified --custom-strings flag that accepts JSON file paths or inline
JSON for UI customization. This leverages the existing i18n system for
better scalability and maintainability.

Changes:
- Add --custom-strings flag with JSON validation
- Extend i18n system to merge custom strings with defaults
- Remove newly-added individual login/UI flags
- Deprecate legacy --app-name and --welcome-text flags
- Update login route to use unified i18n system
- Add comprehensive tests for new functionality
- Update documentation with migration guide and examples

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Remove migration examples for flags that were never released.
Only --app-name and --welcome-text were in the original codebase
and might be used by existing users.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Replace outdated CS_* environment variable examples with the new
--custom-strings flag approach. Include both inline JSON and
mounted file examples for Docker users.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
@strickvl
Copy link
Author

@code-asher is this in line with what you were thinking?

@strickvl strickvl closed this Jun 21, 2025
@strickvl strickvl reopened this Jun 21, 2025
Copy link
Member

@code-asher code-asher left a comment

Choose a reason for hiding this comment

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

Yes this is exactly along the line I was thinking! Thank you, this is really great.

Comment on lines +84 to +100
### Login page customization

You can customize the login page using the `--custom-strings` flag:

```bash
code-server --custom-strings '{"LOGIN_TITLE": "My Code Server", "WELCOME": "Welcome to my portal"}'
```

Or use a JSON file:
```bash
code-server --custom-strings /path/to/custom-strings.json
```

Legacy individual flags (`--app-name`, `--welcome-text`) are still supported but deprecated.

For detailed customization options and examples, see the [customization guide](./customization.md).

Copy link
Member

Choose a reason for hiding this comment

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

IMO this flag is too niche to earn a callout in the FAQ, so we should delete this.

Comment on lines +64 to +65
You can also customize the login page appearance - see our [customization guide](./customization.md).

Copy link
Member

Choose a reason for hiding this comment

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

I am thinking we append an entry to guide.md instead of the main readme. Something like:

## Internationalization and customization

You can customize some of code-server's strings for either internationalization or customization purposes. See our [customization guide](./customization.md).

@@ -0,0 +1,170 @@
# Login Page Customization
Copy link
Member

@code-asher code-asher Jun 23, 2025

Choose a reason for hiding this comment

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

What do you think about making this more generic rather than login-specific? I also think there is a fair bit of repetition here we could consolidate and I worry that this will get out of sync with the language file. We can also keep the legacy flags around, no need to say we might remove them eventually.

I also think it would be reasonable to keep this text in guide.md rather than linking out to a new file. We have so many markdown files sometimes I get lost. 😅

Maybe something like:

Internationalization and customization

code-server allows you to provide a language file or JSON to configure certain strings. This can be used for both internationalization and customization.

For example:

code-server --i18n /custom-strings.json
code-server --i18n `{"WELCOME": "{{app}} ログイン"}`

Or this can be done in the config file:

i18n: |
  {
    "WELCOME": "Welcome to the {{app}} Development Portal",
  }

You can combine this with the --locale flag to configure language support for both code-server and VS Code in cases where code-server has no support but VS Code does. If you are using this for internationalization, please consider sending us a pull request to contribute it to i18n/locales.

Available keys and placeholders

Refer to ../src/node/i18n/locales/en.json for a full list of the available keys for translations. Note that the only placeholders supported for each key are the ones used in the default string.

The --app-name flag controls the {{app}} placeholder in templates. If you want to change the name, you can either:

  1. Set --app-name (potentially alongside --i18n)
  2. Use --i18n and hardcode the name in your strings

Legacy flag

The --welcome-text flag is now deprecated. Use the WELCOME key instead.

@@ -287,6 +287,34 @@ docker run -it --name code-server -p 127.0.0.1:8080:8080 \
codercom/code-server:latest
```

### Customizing the login page
Copy link
Member

Choose a reason for hiding this comment

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

This guide is just for installing code-server, I think we should omit this. Also the customization can be applied to any install method, not just Docker, so we would want to generalize it anyway.

@@ -93,6 +93,7 @@ export interface UserProvidedArgs extends UserProvidedCodeArgs {
"app-name"?: string
"welcome-text"?: string
"abs-proxy-base-path"?: string
"custom-strings"?: string
Copy link
Member

Choose a reason for hiding this comment

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

What do you think about calling it i18n?

Comment on lines +29 to +32
export async function loadCustomStrings(customStringsArg?: string): Promise<void> {
if (!customStringsArg) {
return
}
Copy link
Member

Choose a reason for hiding this comment

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

We already check the arg before passing it in, so we can make this non-optional.

Suggested change
export async function loadCustomStrings(customStringsArg?: string): Promise<void> {
if (!customStringsArg) {
return
}
export async function loadCustomStrings(customStringsArg: string): Promise<void> {

Comment on lines +127 to +135
if (args["custom-strings"]) {
try {
await loadCustomStrings(args["custom-strings"])
logger.info("Loaded custom strings")
} catch (error) {
logger.error("Failed to load custom strings", field("error", error))
throw error
}
}
Copy link
Member

Choose a reason for hiding this comment

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

We already add Failed to load custom strings to the error, and any errors thrown here will already be logged (I think?), so I am thinking we remove the catch to avoid logging the error twice.

Suggested change
if (args["custom-strings"]) {
try {
await loadCustomStrings(args["custom-strings"])
logger.info("Loaded custom strings")
} catch (error) {
logger.error("Failed to load custom strings", field("error", error))
throw error
}
}
if (args["custom-strings"]) {
await loadCustomStrings(args["custom-strings"])
logger.info("Loaded custom strings")
}

Comment on lines +52 to +55
delete process.env.CS_LOGIN_TITLE
delete process.env.CS_LOGIN_ENV_PASSWORD_MSG
delete process.env.CS_PASSWORD_PLACEHOLDER
delete process.env.CS_SUBMIT_TEXT
Copy link
Member

Choose a reason for hiding this comment

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

These env vars do not exist anymore, do they?


// Try to parse as JSON first
try {
customStringsData = JSON.parse(customStringsArg)
Copy link
Member

@code-asher code-asher Jun 23, 2025

Choose a reason for hiding this comment

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

This automatic JSON/filename handling is really convenient but I worry about overloading the flag like this, especially with error handling since we might return a "file not found" error but really the user just mistyped their JSON and forgot the leading {, as one example.

I am not familiar with any precedent we can draw on for inspiration, but I have a few ideas:

  1. We make --i18n take a file always, with no way to provide raw JSON (for now).
  2. We make separate --i18n and --i18n-json flags.
  3. We make --i18n take a file always, and use an environment variable if you want to pass raw JSON.
  4. We make --i18n take a file always, and a new repeatable i18n-var flag. Usage would be code-server i18n-var "WELCOME=my welcome text" i18n-var "LOGIN_TITLE=my title". Something like that. This is kinda nice because there is no need to write JSON on the command line, but it feels a bit clunky.

Thoughts? I think maybe we skip the raw JSON input for now and figure it out later if folks ask for it, unless you feel strongly about including it!

@@ -146,5 +146,70 @@ describe("login", () => {
expect(resp.status).toBe(200)
expect(htmlContent).toContain(`欢迎来到 code-server`)
})

it("should return custom login title", async () => {
Copy link
Member

Choose a reason for hiding this comment

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

Looks like these tests are outdated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants