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" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jfrog-oauth/coder"
version = "1.2.1"
version = "1.2.2"
agent_id = coder_agent.example.id
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"
@ -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.
## 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
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" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jfrog-oauth/coder"
version = "1.2.1"
version = "1.2.2"
agent_id = coder_agent.example.id
jfrog_url = "https://example.jfrog.io"
username_field = "email"
@ -76,7 +85,7 @@ The [JFrog extension](https://open-vsx.org/extension/JFrog/jfrog-vscode-extensio
module "jfrog" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jfrog-oauth/coder"
version = "1.2.1"
version = "1.2.2"
agent_id = coder_agent.example.id
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"

View File

@ -76,8 +76,27 @@ variable "package_managers" {
}
locals {
# The username field to use for artifactory
username = var.username_field == "email" ? data.coder_workspace_owner.me.email : data.coder_workspace_owner.me.name
jwt_parts = try(split(".", data.coder_external_auth.jfrog.access_token), [])
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]
common_values = {
JFROG_URL = var.jfrog_url