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
21 changes: 21 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,27 @@ jobs:
with:
username: ${{ vars.DOCKERHUB_OIDC_USERNAME }}

registry-auth-oidc:
permissions:
contents: read
id-token: write
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
-
name: Login to registries
uses: ./
env:
DOCKERHUB_OIDC_CONNECTIONID: ${{ vars.DOCKERHUB_OIDC_CONNECTIONID }}
with:
registry-auth: |
- username: ${{ vars.DOCKERHUB_OIDC_USERNAME }}
- registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

ecr:
runs-on: ${{ matrix.os }}
strategy:
Expand Down
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,38 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
```

Docker Hub OIDC can also be used with `registry-auth`. Grant `id-token: write`,
set `DOCKERHUB_OIDC_CONNECTIONID`, pass the Docker Hub organization name as
`username`, and omit `password` for the Docker Hub object:

```yaml
name: ci

on:
push:
branches: main

permissions:
contents: read
id-token: write

jobs:
login:
runs-on: ubuntu-latest
steps:
-
name: Login to registries
uses: docker/login-action@v4
env:
DOCKERHUB_OIDC_CONNECTIONID: ${{ vars.DOCKERHUB_OIDC_CONNECTIONID }}
with:
registry-auth: |
- username: ${{ vars.DOCKERHUB_ORGANIZATION }}
- registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
```

### Set scopes for the authentication token

The `scope` input allows limiting registry credentials to a specific repository
Expand Down
19 changes: 19 additions & 0 deletions __tests__/context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,25 @@ test('getAuthList skips secret masking when registry-auth password is absent', a
});
});

test('getAuthList supports password-less Docker Hub registry-auth for OIDC', async () => {
const [auth] = getAuthList({
registry: '',
username: '',
password: '',
scope: '',
ecr: '',
logout: true,
registryAuth: '- username: docker-org\n'
});

expect(auth).toMatchObject({
registry: 'docker.io',
username: 'docker-org',
password: undefined,
ecr: 'auto'
});
});

test('getAuthList masks registry-auth password when present', async () => {
const stdoutWriteSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
getAuthList({
Expand Down
41 changes: 39 additions & 2 deletions __tests__/docker.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import {expect, test, vi} from 'vitest';
import {afterEach, expect, test, vi} from 'vitest';

import {Docker} from '@docker/actions-toolkit/lib/docker/docker.js';

import {loginStandard, logout} from '../src/docker.js';
import {login, loginStandard, logout} from '../src/docker.js';
import * as dockerhub from '../src/dockerhub.js';

afterEach(() => {
vi.restoreAllMocks();
delete process.env.DOCKERHUB_OIDC_CONNECTIONID;
});

test('loginStandard calls exec', async () => {
const execSpy = vi.spyOn(Docker, 'getExecOutput').mockImplementation(async () => {
Expand Down Expand Up @@ -32,6 +38,37 @@ test('loginStandard calls exec', async () => {
});
});

test('login exchanges Docker Hub OIDC token for password-less auth', async () => {
process.env.DOCKERHUB_OIDC_CONNECTIONID = '123e4567-e89b-42d3-a456-426614174000';
const execSpy = vi.spyOn(Docker, 'getExecOutput').mockImplementation(async () => {
return {
exitCode: 0,
stdout: '',
stderr: ''
};
});
const oidcSpy = vi.spyOn(dockerhub, 'getOIDCToken').mockResolvedValue({
username: 'docker-org',
token: 'hub-token'
});

await login({
registry: 'docker.io',
username: 'docker-org',
password: '',
scope: '',
ecr: 'auto',
configDir: ''
});

expect(oidcSpy).toHaveBeenCalledWith('docker.io', 'docker-org');
expect(execSpy).toHaveBeenCalledWith(['login', '--password-stdin', '--username', 'docker-org', 'docker.io'], {
input: Buffer.from('hub-token'),
silent: true,
ignoreReturnCode: true
});
});

test('logout calls exec', async () => {
const execSpy = vi.spyOn(Docker, 'getExecOutput').mockImplementation(async () => {
return {
Expand Down
Loading
Loading