# OAuth 2.0 — Client Credentials

Use this skill when the provider uses the **client credentials grant** — machine-to-machine authentication where the app authenticates itself directly with no user consent or browser redirect.

## When to Use

✅ Use when the provider issues tokens via `grant_type: client_credentials`
✅ Use when there is no `authorizationUrl` — no browser redirect, no user consent
✅ Use for server-to-server integrations accessing app-level or shared data

❌ Do NOT use if a user must authorize access → use `auth-oauth2-code-*` skills instead

**How to verify:** Provider docs show a direct POST to the token endpoint with `grant_type: client_credentials` and no authorization URL.

**Known examples:** Talend, ADP Workforce Now, Greenhouse, GlobalizationPartners

### Exceptions

Some providers support `client_credentials` but only grant limited (e.g. `public`) scopes — not enough for typical integration use cases. In these cases, prefer a Personal Access Token via `auth-custom` instead.

**Known exceptions:** Vimeo (client credentials only grants `public` scope)

---

## How It Works

1. App POSTs to the token endpoint with `grant_type: client_credentials` + credentials
2. Provider returns `access_token` (and usually `expires_in`)
3. When token expires, `refreshAuthentication` repeats the same call — there is no `refresh_token`

---

## Template

> **The map key (`oauth2:` below) is the block's identifier, not a fixed keyword** — gates
> A9/B6 match it against the researched `authMechanisms[].type`, so name it after that
> type verbatim (e.g. `- oauth2_bot_token:`).

```yaml
authentication:
  - oauth2:
      type: oauth2
      label: OAuth 2.0
      grantType: client_credentials
      setupFields:
        - key: clientId
          label: Client ID
          type: text
          required: true
          secret: false
          placeholder: your-client-id
          description: Client ID from your {Provider} app or service account
        - key: clientSecret
          label: Client Secret
          type: password
          required: true
          secret: true
          placeholder: your-client-secret
          description: Client Secret from your {Provider} app or service account
      authorization:
        type: bearer
        token: $.credentials.accessToken
        includeBearer: true
      refreshAuthentication:
        action:
          actionId: refresh_token_{provider}
          categories:
            - internal
          actionType: refresh_token
          label: Generate Access Token
          description: Generate {Provider} access token using client credentials
          steps:
            - stepId: generate_token
              description: Request access token from {Provider}
              stepFunction:
                functionName: request
                parameters:
                  baseUrl: https://provider.com
                  url: /oauth/token
                  method: post
                  authorization:
                    type: basic
                    username: $.credentials.clientId
                    password: $.credentials.clientSecret
                    encoding: base64
                  args:
                    - name: Content-Type
                      value: application/x-www-form-urlencoded
                      in: headers
                    - name: grant_type
                      value: client_credentials
                      in: body
            - stepId: map_token_response
              description: Map access token to credentials
              stepFunction:
                functionName: map_fields
                version: '2'
                parameters:
                  fields:
                    - targetFieldKey: accessToken
                      expression: $.access_token
                      type: string
                    - targetFieldKey: expiresIn
                      expression: $.expires_in
                      type: number
                  dataSource: $.steps.generate_token.output.data
          result:
            data: $.steps.map_token_response.output.data
      environments:
        - key: production
          name: Production
      testActions:
        - action: {action_id}   # e.g. list_users, get_current_user, get_account
          required: true
```

### Token endpoint auth variants

Most providers use Basic Auth (`clientId:clientSecret`). Some send credentials in the body instead:

```yaml
# Body params variant (no Basic Auth header)
stepFunction:
  functionName: request
  parameters:
    baseUrl: ...
    url: ...
    method: post
    authorization:
      type: none
    args:
      - name: Content-Type
        value: application/x-www-form-urlencoded
        in: headers
      - name: grant_type
        value: client_credentials
        in: body
      - name: client_id
        value: $.credentials.clientId
        in: body
      - name: client_secret
        value: $.credentials.clientSecret
        in: body
```

> Check provider docs: if the token endpoint shows `Authorization: Basic ...` in the example, use `type: basic`. If it shows `client_id` and `client_secret` in the request body, use `type: none` with body args.

---

## Real Examples

### Talend

Dynamic `baseUrl` from `credentials.region`. Basic Auth on token endpoint.

```yaml
authentication:
  - oauth2:
      type: oauth2
      label: OAuth 2.0
      grantType: client_credentials
      setupFields:
        - key: clientId
          label: Client ID
          type: text
          required: true
          secret: false
        - key: clientSecret
          label: Client Secret
          type: password
          required: true
          secret: true
      configFields:
        - key: region
          label: Region
          type: select
          required: true
          options:
            - label: US
              value: us
            - label: EU
              value: eu
            - label: Asia Pacific
              value: ap
            - label: Australia
              value: au
      authorization:
        type: bearer
        token: $.credentials.accessToken
        includeBearer: true
      refreshAuthentication:
        action:
          actionId: refresh_token_talend
          categories:
            - internal
          actionType: refresh_token
          label: Generate Access Token
          description: Generate Talend Cloud access token using client credentials
          steps:
            - stepId: generate_token
              description: Request access token from Talend
              stepFunction:
                functionName: request
                parameters:
                  baseUrl: https://api.${credentials.region}.cloud.talend.com
                  url: /security/oauth/token
                  method: post
                  authorization:
                    type: basic
                    username: $.credentials.clientId
                    password: $.credentials.clientSecret
                    encoding: base64
                  args:
                    - name: Content-Type
                      value: application/x-www-form-urlencoded
                      in: headers
                    - name: grant_type
                      value: client_credentials
                      in: body
            - stepId: map_token_response
              description: Map access token to credentials
              stepFunction:
                functionName: map_fields
                version: '2'
                parameters:
                  fields:
                    - targetFieldKey: accessToken
                      expression: $.access_token
                      type: string
                    - targetFieldKey: expiresIn
                      expression: $.expires_in
                      type: number
                  dataSource: $.steps.generate_token.output.data
          result:
            data: $.steps.map_token_response.output.data
```

`baseUrl: https://api.${credentials.region}.cloud.talend.com`

---

### ADP Workforce Now

Client credentials + mTLS. Adds a `certificates` block at both the auth level and inside the refresh request step. Extra `setupFields` for `certificate` and `privateKey`.

```yaml
# Additional setupFields beyond clientId/clientSecret:
- key: certificate
  label: Certificate
  type: password
  required: true
  secret: true
- key: privateKey
  label: Private Key
  type: password
  required: true
  secret: true

# Auth level — declares mTLS certs for all API requests:
certificates:
  certs:
    - $.credentials.certificate
  keys:
    - $.credentials.privateKey

# Inside the generate_token request step — repeat certs for the token call:
parameters:
  baseUrl: https://accounts.adp.com
  url: /auth/oauth/v2/token
  method: post
  authorization:
    type: basic
    username: $.credentials.clientId
    password: $.credentials.clientSecret
    encoding: base64
  certificates:
    certs:
      - $.credentials.certificate
    keys:
      - $.credentials.privateKey
  args:
    - name: Content-Type
      value: application/x-www-form-urlencoded
      in: headers
    - name: grant_type
      value: client_credentials
      in: body
```

---

## Critical Rules

### MUST:
- ✅ Set `grantType: client_credentials` at the auth level
- ✅ Include `refreshAuthentication` — re-runs the same client credentials call when token expires
- ✅ Send `grant_type: client_credentials` in the request body
- ✅ Set `dataSource: $.steps.generate_token.output.data` in `map_fields`
- ✅ Map both `accessToken` and `expiresIn` — client credentials tokens always expire
- ✅ Use `configFields` (not `setupFields`) for user-specific fields like region, tenant, domain

### MUST NOT:
- ❌ Include `authorizationUrl` or `authorizationParams` — no browser redirect in client credentials
- ❌ Map `refreshToken` — client credentials flow has no refresh token concept
- ❌ Use `dataSource: $` in `map_fields` — reads root context, not the token response step output
- ❌ Omit `refreshAuthentication` — tokens always expire, re-auth is required

## Related Skills

- `auth-oauth2` — parent decision router
- `auth-oauth2-code-longterm` — authorization code, token never expires
- `auth-oauth2-code-refresh` — authorization code with refresh token
- `auth-oauth2-code-exchange` — authorization code with intermediate exchange step
