Skip to content

Commit bfccfd8

Browse files
committed
New OIDC auth method
1 parent 8ac64b8 commit bfccfd8

File tree

3 files changed

+86
-6
lines changed

3 files changed

+86
-6
lines changed

action.yml

+11-1
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,17 @@ branding:
44
icon: 'lock'
55
color: 'blue'
66
inputs:
7+
auth-method:
8+
description: >-
9+
Auth method to use - set this to "oidc" for identity auth via OIDC.
10+
Note that this requires the `id-token: write` permission on the Github
11+
job or workflow. See https://docs.doppler.com/docs/service-account-identities
12+
default: "token"
713
doppler-token:
814
description: >-
915
Doppler Service Token that grants access to a single Config within a Project.
1016
See https://docs.doppler.com/docs/service-tokens
11-
required: true
17+
required: false
1218
doppler-project:
1319
description: >-
1420
Doppler Project
@@ -21,6 +27,10 @@ inputs:
2127
description: >-
2228
Inject secrets as environment variables for subsequent steps if set to `true`.
2329
required: false
30+
doppler-identity-id:
31+
description: >-
32+
Identity to use, required when auth-method is "oidc".
33+
required: false
2434
runs:
2535
using: 'node20'
2636
main: 'index.js'

doppler.js

+58-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { VERSION } from "./meta.js";
88
* @param {string | null} [dopplerConfig]
99
* @returns {() => Promise<Record<string, Record>>}
1010
*/
11-
async function fetch(dopplerToken, dopplerProject, dopplerConfig) {
11+
export async function fetch(dopplerToken, dopplerProject, dopplerConfig) {
1212
return new Promise(function (resolve, reject) {
1313
const encodedAuthData = Buffer.from(`${dopplerToken}:`).toString("base64");
1414
const authHeader = `Basic ${encodedAuthData}`;
@@ -54,4 +54,60 @@ async function fetch(dopplerToken, dopplerProject, dopplerConfig) {
5454
});
5555
}
5656

57-
export default fetch;
57+
/**
58+
* Exchange an OIDC token for a short lived Doppler service account token
59+
* @param {string} identityId
60+
* @param {string} oidcToken
61+
* @returns {() => Promise<string>}
62+
*/
63+
export async function oidcAuth(identityId, oidcToken) {
64+
return new Promise(function (resolve, reject) {
65+
const userAgent = `secrets-fetch-github-action/${VERSION}`;
66+
67+
const url = new URL("https://api.doppler.com/v3/auth/oidc");
68+
const body = JSON.stringify({
69+
identity: identityId,
70+
token: oidcToken
71+
});
72+
73+
const request = https
74+
.request(
75+
url.href,
76+
{
77+
headers: {
78+
"user-agent": userAgent,
79+
"accepts": "application/json",
80+
"Content-Type": "application/json",
81+
"Content-Length": body.length,
82+
},
83+
method: 'POST'
84+
},
85+
(res) => {
86+
let payload = "";
87+
res.on("data", (data) => (payload += data));
88+
res.on("end", () => {
89+
if (res.statusCode === 200) {
90+
resolve(JSON.parse(payload).token);
91+
} else {
92+
try {
93+
const error = JSON.parse(payload).messages.join(" ");
94+
reject(new Error(`Doppler API Error: ${error}`));
95+
} catch (error) {
96+
// In the event an upstream issue occurs and no JSON payload is supplied
97+
reject(new Error(`Doppler API Error: ${res.statusCode} ${res.statusMessage}`));
98+
}
99+
}
100+
});
101+
}
102+
);
103+
104+
request
105+
.on("error", (error) => {
106+
reject(new Error(`Doppler API Error: ${error}`));
107+
});
108+
109+
request.write(body);
110+
111+
request.end()
112+
});
113+
}

index.js

+17-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import core from "@actions/core";
2-
import fetch from "./doppler.js";
2+
import { fetch, oidcAuth } from "./doppler.js";
33

44
// For local testing
55
if (process.env.NODE_ENV === "development" && process.env.DOPPLER_TOKEN) {
@@ -8,11 +8,25 @@ if (process.env.NODE_ENV === "development" && process.env.DOPPLER_TOKEN) {
88
process.env["INPUT_DOPPLER-CONFIG"] = process.env.DOPPLER_CONFIG;
99
}
1010

11+
const AUTH_METHOD = core.getInput("auth-method");
12+
let DOPPLER_TOKEN = "";
13+
14+
if (AUTH_METHOD === "oidc") {
15+
const DOPPLER_IDENTITY_ID = core.getInput("doppler-identity-id", { required: true });
16+
const oidcToken = await core.getIDToken();
17+
core.setSecret(oidcToken);
18+
DOPPLER_TOKEN = await oidcAuth(DOPPLER_IDENTITY_ID, oidcToken)
19+
} else if (AUTH_METHOD === "token") {
20+
DOPPLER_TOKEN = core.getInput("doppler-token", { required: true });
21+
}else {
22+
core.setFailed("Unsupported auth-method");
23+
process.exit();
24+
}
25+
1126
const DOPPLER_META = ["DOPPLER_PROJECT", "DOPPLER_CONFIG", "DOPPLER_ENVIRONMENT"];
12-
const DOPPLER_TOKEN = core.getInput("doppler-token", { required: true });
1327
core.setSecret(DOPPLER_TOKEN);
1428

15-
const IS_SA_TOKEN = DOPPLER_TOKEN.startsWith("dp.sa.");
29+
const IS_SA_TOKEN = DOPPLER_TOKEN.startsWith("dp.sa.") || DOPPLER_TOKEN.startsWith("dp.said.");
1630
const IS_PERSONAL_TOKEN = DOPPLER_TOKEN.startsWith("dp.pt.");
1731
const DOPPLER_PROJECT = (IS_SA_TOKEN || IS_PERSONAL_TOKEN) ? core.getInput("doppler-project") : null;
1832
const DOPPLER_CONFIG = (IS_SA_TOKEN || IS_PERSONAL_TOKEN) ? core.getInput("doppler-config") : null;

0 commit comments

Comments
 (0)