GithubActionsOidcClient.java
/*
* Copyright 2022 The Sigstore Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.sigstore.oidc.client;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.json.GenericJson;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.client.json.webtoken.JsonWebSignature;
import com.google.api.client.util.Key;
import dev.sigstore.http.HttpClients;
import dev.sigstore.http.HttpParams;
import io.grpc.Internal;
import java.io.IOException;
import java.util.Map;
import java.util.logging.Logger;
/**
* Obtain an oidc token from the github execution environment.
* https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect
*/
public class GithubActionsOidcClient implements OidcClient {
private static final Logger log = Logger.getLogger(GithubActionsOidcClient.class.getName());
static final String GITHUB_ACTIONS_KEY = "GITHUB_ACTIONS";
static final String REQUEST_TOKEN_KEY = "ACTIONS_ID_TOKEN_REQUEST_TOKEN";
static final String REQUEST_URL_KEY = "ACTIONS_ID_TOKEN_REQUEST_URL";
private static final String DEFAULT_AUDIENCE = "sigstore";
private final String audience;
private final HttpParams httpParams;
public static Builder builder() {
return new Builder();
}
private GithubActionsOidcClient(HttpParams httpParams, String audience) {
this.audience = audience;
this.httpParams = httpParams;
}
public static class Builder {
private HttpParams httpParams = HttpParams.builder().build();
private String audience = DEFAULT_AUDIENCE;
private Builder() {}
public Builder audience(String audience) {
this.audience = audience;
return this;
}
public Builder httpParams(HttpParams httpParams) {
this.httpParams = httpParams;
return this;
}
public GithubActionsOidcClient build() {
return new GithubActionsOidcClient(httpParams, audience);
}
}
@Override
public boolean isEnabled(Map<String, String> env) {
var githubActions = env.get(GITHUB_ACTIONS_KEY);
if (githubActions == null || githubActions.isEmpty()) {
log.fine("Github env not detected: skipping github actions oidc");
return false;
}
var bearer = env.get(REQUEST_TOKEN_KEY);
var urlBase = env.get(REQUEST_URL_KEY);
if (bearer == null || bearer.isEmpty() || urlBase == null || urlBase.isEmpty()) {
log.info("Github env detected, but github idtoken not found: skipping github actions oidc");
return false;
}
return true;
}
@Override
public OidcToken getIDToken(Map<String, String> env) throws OidcException {
var bearer = env.get(REQUEST_TOKEN_KEY);
var urlBase = env.get(REQUEST_URL_KEY);
if (bearer == null) {
throw new OidcException(
"Could not get github actions environment variable '" + REQUEST_TOKEN_KEY + "'");
}
if (urlBase == null) {
throw new OidcException(
"Could not get github actions environment variable '" + REQUEST_URL_KEY + "'");
}
var url = new GenericUrl(urlBase + "&audience=" + audience);
try {
var req = HttpClients.newRequestFactory(httpParams).buildGetRequest(url);
req.setParser(new GsonFactory().createJsonObjectParser());
req.getHeaders().setAuthorization("Bearer " + bearer);
req.getHeaders().setAccept("application/json; api-version=2.0");
req.getHeaders().setContentType("application/json");
var resp = req.execute().parseAs(GithubOidcJsonResponse.class);
var idToken = resp.getValue();
var jws = JsonWebSignature.parse(new GsonFactory(), idToken);
return ImmutableOidcToken.builder()
.idToken(idToken)
.issuer(jws.getPayload().getIssuer())
.subjectAlternativeName(jws.getPayload().getSubject())
.build();
} catch (IOException e) {
throw new OidcException("Could not obtain github actions oidc token", e);
}
}
@Internal
public static class GithubOidcJsonResponse extends GenericJson {
@Key("value")
private String value;
String getValue() {
return value;
}
}
}