Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ jobs:

- name: Verify step outputs
run: |
if [ -z "${{ steps.buddy_login.outputs.api_key }}" ]; then
echo "Error: Step output api_key is not set"
if [ -z "${{ steps.buddy_login.outputs.token }}" ]; then
echo "Error: Step output token is not set"
exit 1
fi
echo "Success: Step output api_key is set"
echo "Success: Step output token is set"

if [ -z "${{ steps.buddy_login.outputs.api_endpoint }}" ]; then
echo "Error: Step output api_endpoint is not set"
Expand All @@ -74,7 +74,7 @@ jobs:
uses: ./
with:
debug: true
api_key: ${{ secrets.BUDDY_PAT }}
token: ${{ secrets.BUDDY_PAT }}
api_url: ${{ env.TEST_API_URL }}

- name: Verify BUDDY_TOKEN is set
Expand All @@ -95,11 +95,11 @@ jobs:

- name: Verify step outputs
run: |
if [ -z "${{ steps.buddy_login.outputs.api_key }}" ]; then
echo "Error: Step output api_key is not set"
if [ -z "${{ steps.buddy_login.outputs.token }}" ]; then
echo "Error: Step output token is not set"
exit 1
fi
echo "Success: Step output api_key is set"
echo "Success: Step output token is set"

if [ -z "${{ steps.buddy_login.outputs.api_endpoint }}" ]; then
echo "Error: Step output api_endpoint is not set"
Expand Down Expand Up @@ -181,13 +181,13 @@ jobs:
audience: 'this-is-a-very-long-audience-string-that-might-cause-issues-this-is-a-very-long-audience-string-that-might-cause-issues-this-is-a-very-long-audience-string-that-might-cause-issues-this-is-a-very-long-audience-string-that-might-cause-issues'

# Mixed auth params tests
- name: Both api_key and provider_id provided
- name: Both token and provider_id provided
continue-on-error: true
uses: ./
with:
debug: true
api_url: ${{ env.TEST_API_URL }}
api_key: ${{ secrets.BUDDY_PAT }}
token: ${{ secrets.BUDDY_PAT }}
provider_id: ${{ secrets.BUDDY_PROVIDER_ID }}
audience: 'buddy'

Expand All @@ -197,25 +197,25 @@ jobs:
with:
debug: true
api_url: ${{ env.TEST_API_URL }}
api_key: ${{ secrets.BUDDY_PAT }}
token: ${{ secrets.BUDDY_PAT }}
audience: 'buddy'

# Wrong API key tests
- name: PAT - Invalid API key format
- name: PAT - Invalid token format
continue-on-error: true
uses: ./
with:
debug: true
api_url: ${{ env.TEST_API_URL }}
api_key: 'invalid-api-key-format'
token: 'invalid-token-format'

- name: PAT - Empty API key
- name: PAT - Empty token
continue-on-error: true
uses: ./
with:
debug: true
api_url: ${{ env.TEST_API_URL }}
api_key: ''
token: ''

# Missing required params tests
- name: No authentication params
Expand Down
2 changes: 1 addition & 1 deletion CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1 +1 @@
* @buddy-works
* @buddy
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Login to Buddy
uses: buddy/login@v1
with:
api_key: ${{ secrets.BUDDY_API_KEY }}
token: ${{ secrets.BUDDY_PAT }}
region: 'EU' # or 'US' (default)

- name: Use Buddy API
Expand All @@ -34,6 +34,7 @@ jobs:
### Method 2: OIDC Authentication (No Stored Secrets)

#### Setup

1. Configure GitHub as an OIDC provider in your Buddy workspace
2. Note the provider UUID
3. Add to your workflow with `id-token: write` permission
Expand Down Expand Up @@ -80,21 +81,21 @@ jobs:

## Inputs

| Input | Required | Description |
| ------------- | -------- | ------------------------------------------------------------------------------------------------ |
| `api_key` | No* | Buddy API key (required if not using OIDC). Store in GitHub Secrets. UUID v4 format. |
| `provider_id` | No* | UUID of your Buddy OIDC provider (required if not using API key) |
| `region` | No** | Buddy region: `EU` or `US` (default: `US`) |
| `api_url` | No** | Custom API URL for on-premise installations |
| `audience` | No | OIDC audience (uses GitHub default if not set) |
| `debug` | No | Enable debug logging (`true`/`false`) |
| Input | Required | Description |
| ------------- | -------- | ------------------------------------------------------------------------------------------------------ |
| `token` | Yes\* | Buddy PAT (Personal Access Token) required if not using OIDC. Store in GitHub Secrets. UUID v4 format. |
| `provider_id` | Yes\* | UUID of your Buddy OIDC provider (required if not using token) |
| `region` | No\*\* | Buddy region: `EU` or `US` (default: `US`) |
| `api_url` | No\*\* | Custom API URL for on-premise installations |
| `audience` | No | OIDC audience (uses GitHub default if not set) |
| `debug` | No | Enable debug logging (`true`/`false`) |

\* Either `api_key` or `provider_id` must be provided
\** Either `region` or `api_url` must be provided
\* Either `token` or `provider_id` must be provided
\*\* Either `region` or `api_url` must be provided

## Outputs

- `api_key` - The Buddy API key
- `token` - The Buddy PAT (Personal Access Token)
- `api_endpoint` - The Buddy API endpoint URL
- Environment variables set:
- `BUDDY_TOKEN` - The API key
Expand Down
8 changes: 4 additions & 4 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ inputs:
provider_id:
description: 'UUID of your Buddy OIDC provider configuration. Required for OIDC authentiaction. Recommendation: Store authentication tokens in GitHub Secrets to keep credentials secure and out of your codebase.'
required: false
api_key:
description: 'The key when using API key login. Required if not using OIDC authentication. Recommendation: Store authentication tokens in GitHub Secrets to keep credentials secure and out of your codebase.'
token:
description: 'The PAT (Personal Access Token). Required if not using OIDC authentication. Recommendation: Store authentication tokens in GitHub Secrets to keep credentials secure and out of your codebase.'
required: false
region:
description: 'Buddy region (EU or US). Cannot be used with api_url.'
Expand All @@ -25,8 +25,8 @@ inputs:
description: 'Enable debug logging for troubleshooting'
required: false
outputs:
api_key:
description: 'Buddy API key that can be used to authenticate API requests in subsequent workflow steps. It is also exported as BUDDY_TOKEN variable'
token:
description: 'Buddy token that can be used to authenticate API requests in subsequent workflow steps. It is also exported as BUDDY_TOKEN variable'
api_endpoint:
description: 'The URL to Buddy API endpoint. It is also exported as BUDDY_API_ENDPOINT variable.'
branding:
Expand Down
2 changes: 1 addition & 1 deletion dist/index.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions src/api/buddy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,9 @@ export async function exchangeTokenWithBuddy(
throw new Error(`Invalid response format: Not a valid token or JSON`)
}

const api_key = data.token || data.access_token || data.buddy_token
const token = data.token || data.access_token || data.buddy_token

if (!api_key || typeof api_key !== 'string') {
if (!token || typeof token !== 'string') {
logger.debug('Response was valid JSON but no token field found')
throw new Error(
'No token found in response. Expected fields: token, access_token, or buddy_token',
Expand All @@ -196,7 +196,7 @@ export async function exchangeTokenWithBuddy(

logger.debug(`Token successfully extracted from JSON response`)

return api_key
return token
} catch (error) {
if (error instanceof Error) {
throw new Error(`Token exchange failed: ${error.message}`)
Expand Down
8 changes: 4 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { login } from '@/login'
import { normalizeError } from '@/utils/error/normalizeError'

login()
.then(({ api_key, api_endpoint }) => {
setSecret(api_key)
exportVariable('BUDDY_TOKEN', api_key)
.then(({ token, api_endpoint }) => {
setSecret(token)
exportVariable('BUDDY_TOKEN', token)
exportVariable('BUDDY_API_ENDPOINT', api_endpoint)
setOutput('api_key', api_key)
setOutput('token', token)
setOutput('api_endpoint', api_endpoint)
process.exit(0)
})
Expand Down
8 changes: 4 additions & 4 deletions src/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,19 @@ export async function login(): Promise<IOutputs> {
api_endpoint = API_URL.US
}

if ('api_key' in inputs) {
if ('token' in inputs) {
return {
api_key: inputs.api_key,
token: inputs.token,
api_endpoint,
}
}

// Handle OIDC authentication
const jwt = await getIDToken(inputs.audience)
const api_key = await exchangeTokenWithBuddy(inputs, jwt)
const token = await exchangeTokenWithBuddy(inputs, jwt)

return {
api_key,
token,
api_endpoint,
}
}
2 changes: 1 addition & 1 deletion src/types/inputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type IInputsOIDCBase = IInputsAuthBase & {
}

type IInputsPATBase = IInputsAuthBase & {
api_key: `${string}-${string}-${string}-${string}-${string}`
token: `${string}-${string}-${string}-${string}-${string}`
}

type IInputsOIDCWithRegion = IInputsOIDCBase & {
Expand Down
2 changes: 1 addition & 1 deletion src/types/outputs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export interface IOutputs {
api_key: string
token: string
api_endpoint: string
}
32 changes: 16 additions & 16 deletions src/utils/action/getInputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { isActionDebug } from '@/utils/action/isActionDebug'
* @throws Error if any input is invalid
*/
export function getInputs(): IInputs {
const api_key = getInput('api_key') || undefined
const token = getInput('token') || undefined
const provider_id = getInput('provider_id') || undefined
const audience = getInput('audience') || undefined
const api_url = getInput('api_url') || undefined
Expand All @@ -24,30 +24,30 @@ export function getInputs(): IInputs {
}

// Determine authentication method
// Prioritize API key authentication
if (api_key) {
if (!isApiKeyValid(api_key)) {
throw new Error('Invalid API key: cannot be empty')
// Prioritize token authentication
if (token) {
if (!isTokenValid(token)) {
throw new Error('Invalid token: cannot be empty')
}

// API key auth also needs region or api_url
// Token auth also needs region or api_url
if (api_url) {
if (!isApiUrlValid(api_url)) {
throw new Error('Invalid API URL format. Must be a valid HTTPS URL.')
}

return {
api_key,
token,
api_url,
audience,
debug,
}
}

// Fall back to region for API key auth
// Fall back to region for token auth
if (!region) {
throw new Error(
'Either api_url or region must be provided when using API key authentication.',
'Either api_url or region must be provided when using token authentication.',
)
}

Expand All @@ -58,7 +58,7 @@ export function getInputs(): IInputs {
}

return {
api_key,
token,
region,
audience,
debug,
Expand All @@ -68,7 +68,7 @@ export function getInputs(): IInputs {
// Fall back to OIDC authentication
if (!provider_id) {
throw new Error(
'Either api_key or provider_id must be provided for authentication.',
'Either token or provider_id must be provided for authentication.',
)
}

Expand Down Expand Up @@ -132,17 +132,17 @@ function isProviderIdValid(
return true
}

function isApiKeyValid(
apiKey: string,
): apiKey is `${string}-${string}-${string}-${string}-${string}` {
function isTokenValid(
token: string,
): token is `${string}-${string}-${string}-${string}-${string}` {
// Just check it's not empty and has reasonable length
// Let the backend validate the actual format
if (!apiKey || apiKey.trim().length === 0) {
if (!token || token.trim().length === 0) {
return false
}

// Reasonable max length check
if (apiKey.length > 255) {
if (token.length > 255) {
return false
}

Expand Down
Loading