Skip to content

Commit 27f76df

Browse files
thboopsouravchandukaTingluoHuang
authored
Full release of actions/core 1.6.0 with oidc behavior (actions#919)
* OIDC Client for actions/core Co-authored-by: Sourav Chanduka <[email protected]> Co-authored-by: Sourav Chanduka <[email protected]> Co-authored-by: Tingluo Huang <[email protected]>
1 parent 60145e4 commit 27f76df

File tree

8 files changed

+22247
-29
lines changed

8 files changed

+22247
-29
lines changed

package-lock.json

Lines changed: 22033 additions & 26 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/core/README.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,3 +262,51 @@ var pid = core.getState("pidToKill");
262262
263263
process.kill(pid);
264264
```
265+
266+
#### OIDC Token
267+
268+
You can use these methods to interact with the GitHub OIDC provider and get a JWT ID token which would help to get access token from third party cloud providers.
269+
270+
**Method Name**: getIDToken()
271+
272+
**Inputs**
273+
274+
audience : optional
275+
276+
**Outputs**
277+
278+
A [JWT](https://jwt.io/) ID Token
279+
280+
In action's `main.ts`:
281+
```js
282+
const core = require('@actions/core');
283+
async function getIDTokenAction(): Promise<void> {
284+
285+
const audience = core.getInput('audience', {required: false})
286+
287+
const id_token1 = await core.getIDToken() // ID Token with default audience
288+
const id_token2 = await core.getIDToken(audience) // ID token with custom audience
289+
290+
// this id_token can be used to get access token from third party cloud providers
291+
}
292+
getIDTokenAction()
293+
```
294+
295+
In action's `actions.yml`:
296+
297+
```yaml
298+
name: 'GetIDToken'
299+
description: 'Get ID token from Github OIDC provider'
300+
inputs:
301+
audience:
302+
description: 'Audience for which the ID token is intended for'
303+
required: false
304+
outputs:
305+
id_token1:
306+
description: 'ID token obtained from OIDC provider'
307+
id_token2:
308+
description: 'ID token obtained from OIDC provider'
309+
runs:
310+
using: 'node12'
311+
main: 'dist/index.js'
312+
```

packages/core/RELEASES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# @actions/core Releases
22

3+
### 1.6.0
4+
- [Added OIDC Client function `getIDToken`](https://github.com/actions/toolkit/pull/919)
5+
- [Added `file` parameter to `AnnotationProperties`](https://github.com/actions/toolkit/pull/896)
6+
37
### 1.5.0
48
- [Added support for notice annotations and more annotation fields](https://github.com/actions/toolkit/pull/855)
59

packages/core/__tests__/core.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as fs from 'fs'
22
import * as os from 'os'
33
import * as path from 'path'
44
import * as core from '../src/core'
5+
import {HttpClient} from '@actions/http-client'
56
import {toCommandProperties} from '../src/utils'
67

78
/* eslint-disable @typescript-eslint/unbound-method */
@@ -469,3 +470,20 @@ function verifyFileCommand(command: string, expectedContents: string): void {
469470
fs.unlinkSync(filePath)
470471
}
471472
}
473+
474+
function getTokenEndPoint(): string {
475+
return 'https://vstoken.actions.githubusercontent.com/.well-known/openid-configuration'
476+
}
477+
478+
describe('oidc-client-tests', () => {
479+
it('Get Http Client', async () => {
480+
const http = new HttpClient('actions/oidc-client')
481+
expect(http).toBeDefined()
482+
})
483+
484+
it('HTTP get request to get token endpoint', async () => {
485+
const http = new HttpClient('actions/oidc-client')
486+
const res = await http.get(getTokenEndPoint())
487+
expect(res.message.statusCode).toBe(200)
488+
})
489+
})

packages/core/package-lock.json

Lines changed: 50 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/core/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@actions/core",
3-
"version": "1.5.0",
3+
"version": "1.6.0",
44
"description": "Actions core lib",
55
"keywords": [
66
"github",
@@ -35,6 +35,9 @@
3535
"bugs": {
3636
"url": "https://github.com/actions/toolkit/issues"
3737
},
38+
"dependencies": {
39+
"@actions/http-client": "^1.0.11"
40+
},
3841
"devDependencies": {
3942
"@types/node": "^12.0.2"
4043
}

packages/core/src/core.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import {toCommandProperties, toCommandValue} from './utils'
55
import * as os from 'os'
66
import * as path from 'path'
77

8+
import {OidcClient} from './oidc-utils'
9+
810
/**
911
* Interface for getInput options
1012
*/
@@ -353,3 +355,7 @@ export function saveState(name: string, value: any): void {
353355
export function getState(name: string): string {
354356
return process.env[`STATE_${name}`] || ''
355357
}
358+
359+
export async function getIDToken(aud?: string): Promise<string> {
360+
return await OidcClient.getIDToken(aud)
361+
}

packages/core/src/oidc-utils.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/* eslint-disable @typescript-eslint/no-extraneous-class */
2+
import * as actions_http_client from '@actions/http-client'
3+
import {IRequestOptions} from '@actions/http-client/interfaces'
4+
import {HttpClient} from '@actions/http-client'
5+
import {BearerCredentialHandler} from '@actions/http-client/auth'
6+
import {debug, setSecret} from './core'
7+
interface TokenResponse {
8+
value?: string
9+
}
10+
11+
export class OidcClient {
12+
private static createHttpClient(
13+
allowRetry = true,
14+
maxRetry = 10
15+
): actions_http_client.HttpClient {
16+
const requestOptions: IRequestOptions = {
17+
allowRetries: allowRetry,
18+
maxRetries: maxRetry
19+
}
20+
21+
return new HttpClient(
22+
'actions/oidc-client',
23+
[new BearerCredentialHandler(OidcClient.getRequestToken())],
24+
requestOptions
25+
)
26+
}
27+
28+
private static getRequestToken(): string {
29+
const token = process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN']
30+
if (!token) {
31+
throw new Error(
32+
'Unable to get ACTIONS_ID_TOKEN_REQUEST_TOKEN env variable'
33+
)
34+
}
35+
return token
36+
}
37+
38+
private static getIDTokenUrl(): string {
39+
const runtimeUrl = process.env['ACTIONS_ID_TOKEN_REQUEST_URL']
40+
if (!runtimeUrl) {
41+
throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable')
42+
}
43+
return runtimeUrl
44+
}
45+
46+
private static async getCall(id_token_url: string): Promise<string> {
47+
const httpclient = OidcClient.createHttpClient()
48+
49+
const res = await httpclient
50+
.getJson<TokenResponse>(id_token_url)
51+
.catch(error => {
52+
throw new Error(
53+
`Failed to get ID Token. \n
54+
Error Code : ${error.statusCode}\n
55+
Error Message: ${error.result.message}`
56+
)
57+
})
58+
59+
const id_token = res.result?.value
60+
if (!id_token) {
61+
throw new Error('Response json body do not have ID Token field')
62+
}
63+
return id_token
64+
}
65+
66+
static async getIDToken(audience?: string): Promise<string> {
67+
try {
68+
// New ID Token is requested from action service
69+
let id_token_url: string = OidcClient.getIDTokenUrl()
70+
if (audience) {
71+
const encodedAudience = encodeURIComponent(audience)
72+
id_token_url = `${id_token_url}&audience=${encodedAudience}`
73+
}
74+
75+
debug(`ID token url is ${id_token_url}`)
76+
77+
const id_token = await OidcClient.getCall(id_token_url)
78+
setSecret(id_token)
79+
return id_token
80+
} catch (error) {
81+
throw new Error(`Error message: ${error.message}`)
82+
}
83+
}
84+
}

0 commit comments

Comments
 (0)