Skip to content
Open
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
46 changes: 41 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ ID. Follow the steps in the

The action requires `id-token: write` permission so the workflow can request a
GitHub OIDC token. The exchanged Docker token is returned as the `token` output
and is automatically masked in workflow logs.
and is masked in workflow logs by default.

```yaml
jobs:
Expand All @@ -48,12 +48,48 @@ jobs:
password: ${{ steps.docker_oidc.outputs.token }}
```

## Passing a token between jobs

GitHub skips job outputs containing masked secrets. To pass the token to another
job or reusable workflow, set `mask-token: 'false'` in the producing job, see
the example below:

```yaml
jobs:
docker-token:
runs-on: ubuntu-latest
permissions:
id-token: write
outputs:
token: ${{ steps.oidc.outputs.token }}
steps:
- name: Get Docker token
id: oidc
uses: docker/oidc-action@v1
with:
connection-id: ${{ vars.DOCKERHUB_OIDC_CONNECTIONID }}
mask-token: 'false'

publish:
needs: docker-token
uses: ./.github/workflows/publish.yaml
secrets:
registry-password: ${{ needs.docker-token.outputs.token }}
```

Disabling masking removes automatic log redaction of the Docker token in the
producing job. Avoid printing it and mask it in any receiving job before use.

When you control the consuming job, prefer requesting a fresh token there and
keep masking enabled.

## Inputs

| Input | Required | Default | Description |
| --------------- | -------- | ------- | ------------------------------------------------------------------------------------------------ |
| `connection-id` | Yes | — | The OIDC connection ID from your Docker organization. Must be a v4 UUID. |
| `expires-in` | No | `300` | Lifetime of the returned token, in seconds. Must be between `300` (5 min) and `21600` (6 hours). |
| Input | Required | Default | Description |
| --------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------- |
| `connection-id` | Yes | — | The OIDC connection ID from your Docker organization. Must be a v4 UUID. |
| `expires-in` | No | `300` | Lifetime of the returned token, in seconds. Must be between `300` (5 min) and `21600` (6 hours). |
| `mask-token` | No | `true` | Mask the returned Docker token in workflow logs. Set to `false` to allow forwarding it as a job output. |

## Outputs

Expand Down
1 change: 1 addition & 0 deletions __fixtures__/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { jest } from '@jest/globals';
export const debug = jest.fn<typeof core.debug>();
export const error = jest.fn<typeof core.error>();
export const info = jest.fn<typeof core.info>();
export const getBooleanInput = jest.fn<typeof core.getBooleanInput>();
export const getInput = jest.fn<typeof core.getInput>();
export const setOutput = jest.fn<typeof core.setOutput>();
export const setSecret = jest.fn<typeof core.setSecret>();
Expand Down
12 changes: 12 additions & 0 deletions __tests__/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ function mockInput(overrides: { [key: string]: string } = {}) {
const values: { [key: string]: string } = {
'connection-id': uuidv4(),
'expires-in': '300',
'mask-token': 'true',
...overrides
};

core.getInput.mockImplementation((name) => values[name] ?? '');
core.getBooleanInput.mockImplementation((name) => values[name] === 'true');
}

/**
Expand Down Expand Up @@ -148,6 +150,16 @@ describe('main.ts', () => {
);
});

it('Exports an unmasked token when mask-token is false', async () => {
mockInput({ 'mask-token': 'false' });
mockTokenExchange(200, { access_token: 'test_access_token' });
await run();
expect(core.setFailed).not.toHaveBeenCalled();
expect(core.setSecret).not.toHaveBeenCalled();
expect(core.setOutput).toHaveBeenCalledWith('token', 'test_access_token');
expect(core.getBooleanInput).toHaveBeenCalledWith('mask-token');
});

it('Falls back to the deprecated snake_case inputs', async () => {
const connectionId = uuidv4();
mockInput({
Expand Down
6 changes: 6 additions & 0 deletions action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ inputs:
set a custom expiration time for the token. Value must be a min of 300
(5 minutes) and a max of 21600 (6 hours). The max is to match the maximum
allowed by GitHub hosted runners. Default is 300.
mask-token:
description: |
Mask the returned Docker access token in workflow logs. Set to false to
pass the token between jobs as a job output. Disabling masking means the
token will not be automatically redacted in the producing job logs.
default: 'true'
connection_id:
description: |
The connection identifier to use for the OIDC flow. This can be found in
Expand Down
28 changes: 26 additions & 2 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const url = 'https://identity.docker.com/oauth/token';
type Input = {
connectionId: string;
expiresIn: number;
maskToken: boolean;
};

type ResponseBody = {
Expand Down Expand Up @@ -116,7 +117,9 @@ export async function run(): Promise<void> {

const body = (await resp.json()) as ResponseBody;

core.setSecret(body.access_token); // redacted in workflow logs
if (input.maskToken) {
core.setSecret(body.access_token); // redacted in workflow logs
}
core.setOutput('token', body.access_token);
} catch (e) {
if (e instanceof Error) {
Expand Down Expand Up @@ -160,6 +163,7 @@ function getInput(): Input {

return {
connectionId,
expiresIn
expiresIn,
maskToken: core.getBooleanInput('mask-token')
};
}
Loading