fix: jfrog oauth username extraction from oauth jwt token (#539)

## Description

Add username extraction from jfrog JWT OAuth token with fallback to
coder username.
<!-- Briefly describe what this PR does and why -->

## Type of Change

- [ ] New module
- [ ] New template
- [X] Bug fix
- [ ] Feature/enhancement
- [ ] Documentation
- [ ] Other

## Module Information

<!-- Delete this section if not applicable -->

**Path:** `registry/coder/modules/jfrog-oauth`  
**New version:** `v1.2.2`  
**Breaking change:** [ ] Yes [X] No

## Template Information

<!-- Delete this section if not applicable -->

**Path:** `registry/[namespace]/templates/[template-name]`

## Testing & Validation

- [X] Tests pass (`bun test`)
- [X] Code formatted (`bun fmt`)
- [X] Changes tested locally

## Related Issues

<!-- Link related issues or write "None" if not applicable -->
This commit is contained in:
DevCats 2025-11-17 13:28:05 -05:00 committed by GitHub
parent e11ed2d7ae
commit 71c84a8bb2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 33 additions and 5 deletions

View File

@ -16,7 +16,7 @@ Install the JF CLI and authenticate package managers with Artifactory using OAut
module "jfrog" { module "jfrog" {
count = data.coder_workspace.me.start_count count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jfrog-oauth/coder" source = "registry.coder.com/coder/jfrog-oauth/coder"
version = "1.2.1" version = "1.2.2"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
jfrog_url = "https://example.jfrog.io" jfrog_url = "https://example.jfrog.io"
username_field = "username" # If you are using GitHub to login to both Coder and Artifactory, use username_field = "username" username_field = "username" # If you are using GitHub to login to both Coder and Artifactory, use username_field = "username"
@ -39,6 +39,15 @@ module "jfrog" {
This module is usable by JFrog self-hosted (on-premises) Artifactory as it requires configuring a custom integration. This integration benefits from Coder's [external-auth](https://coder.com/docs/v2/latest/admin/external-auth) feature and allows each user to authenticate with Artifactory using an OAuth flow and issues user-scoped tokens to each user. For configuration instructions, see this [guide](https://coder.com/docs/v2/latest/guides/artifactory-integration#jfrog-oauth) on the Coder documentation. This module is usable by JFrog self-hosted (on-premises) Artifactory as it requires configuring a custom integration. This integration benefits from Coder's [external-auth](https://coder.com/docs/v2/latest/admin/external-auth) feature and allows each user to authenticate with Artifactory using an OAuth flow and issues user-scoped tokens to each user. For configuration instructions, see this [guide](https://coder.com/docs/v2/latest/guides/artifactory-integration#jfrog-oauth) on the Coder documentation.
## Username Handling
The module automatically extracts your JFrog username directly from the OAuth token's JWT payload. This preserves special characters like dots (`.`), hyphens (`-`), and accented characters that Coder normalizes in usernames.
**Priority order:**
1. **JWT extraction** (default) - Extracts username from OAuth token, preserving special characters
2. **Fallback to `username_field`** - If JWT extraction fails, uses Coder username or email
## Examples ## Examples
Configure the Python pip package manager to fetch packages from Artifactory while mapping the Coder email to the Artifactory username. Configure the Python pip package manager to fetch packages from Artifactory while mapping the Coder email to the Artifactory username.
@ -47,7 +56,7 @@ Configure the Python pip package manager to fetch packages from Artifactory whil
module "jfrog" { module "jfrog" {
count = data.coder_workspace.me.start_count count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jfrog-oauth/coder" source = "registry.coder.com/coder/jfrog-oauth/coder"
version = "1.2.1" version = "1.2.2"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
jfrog_url = "https://example.jfrog.io" jfrog_url = "https://example.jfrog.io"
username_field = "email" username_field = "email"
@ -76,7 +85,7 @@ The [JFrog extension](https://open-vsx.org/extension/JFrog/jfrog-vscode-extensio
module "jfrog" { module "jfrog" {
count = data.coder_workspace.me.start_count count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jfrog-oauth/coder" source = "registry.coder.com/coder/jfrog-oauth/coder"
version = "1.2.1" version = "1.2.2"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
jfrog_url = "https://example.jfrog.io" jfrog_url = "https://example.jfrog.io"
username_field = "username" # If you are using GitHub to login to both Coder and Artifactory, use username_field = "username" username_field = "username" # If you are using GitHub to login to both Coder and Artifactory, use username_field = "username"

View File

@ -76,8 +76,27 @@ variable "package_managers" {
} }
locals { locals {
# The username field to use for artifactory jwt_parts = try(split(".", data.coder_external_auth.jfrog.access_token), [])
username = var.username_field == "email" ? data.coder_workspace_owner.me.email : data.coder_workspace_owner.me.name jwt_payload = try(local.jwt_parts[1], "")
payload_padding = local.jwt_payload == "" ? "" : (
length(local.jwt_payload) % 4 == 0 ? "" :
length(local.jwt_payload) % 4 == 2 ? "==" :
length(local.jwt_payload) % 4 == 3 ? "=" :
""
)
jwt_username = try(
regex(
"/users/([^/]+)",
jsondecode(base64decode("${local.jwt_payload}${local.payload_padding}"))["sub"]
)[0],
""
)
username = coalesce(
local.jwt_username != "" ? local.jwt_username : null,
var.username_field == "email" ? data.coder_workspace_owner.me.email : data.coder_workspace_owner.me.name
)
jfrog_host = split("://", var.jfrog_url)[1] jfrog_host = split("://", var.jfrog_url)[1]
common_values = { common_values = {
JFROG_URL = var.jfrog_url JFROG_URL = var.jfrog_url