// Copyright 2022 Luca Casonato. All rights reserved. MIT license. /** * Security Token Service API Client for Deno * ========================================== * * The Security Token Service exchanges Google or third-party credentials for a short-lived access token to Google Cloud resources. * * Docs: http://cloud.google.com/iam/docs/workload-identity-federation * Source: https://googleapis.deno.dev/v1/sts:v1.ts */ import { auth, CredentialsClient, GoogleAuth, request } from "/_/base@v1/mod.ts"; export { auth, GoogleAuth }; export type { CredentialsClient }; /** * The Security Token Service exchanges Google or third-party credentials for a * short-lived access token to Google Cloud resources. */ export class sts { #client: CredentialsClient | undefined; #baseUrl: string; constructor(client?: CredentialsClient, baseUrl: string = "https://sts.googleapis.com/") { this.#client = client; this.#baseUrl = baseUrl; } /** * Exchanges a credential for a Google OAuth 2.0 access token. The token * asserts an external identity within an identity pool, or it applies a * Credential Access Boundary to a Google access token. Note that workforce * pools do not support Credential Access Boundaries. When you call this * method, do not send the `Authorization` HTTP header in the request. This * method does not require the `Authorization` header, and using the header * can cause the request to fail. * */ async v1Token(req: GoogleIdentityStsV1ExchangeTokenRequest): Promise { const url = new URL(`${this.#baseUrl}v1/token`); const body = JSON.stringify(req); const data = await request(url.href, { client: this.#client, method: "POST", body, }); return deserializeGoogleIdentityStsV1ExchangeTokenResponse(data); } } /** * Associates `members`, or principals, with a `role`. */ export interface GoogleIamV1Binding { /** * The condition that is associated with this binding. If the condition * evaluates to `true`, then this binding applies to the current request. If * the condition evaluates to `false`, then this binding does not apply to the * current request. However, a different role binding might grant the same * role to one or more of the principals in this binding. To learn which * resources support conditions in their IAM policies, see the [IAM * documentation](https://cloud.google.com/iam/help/conditions/resource-policies). */ condition?: GoogleTypeExpr; /** * Specifies the principals requesting access for a Google Cloud resource. * `members` can have the following values: * `allUsers`: A special identifier * that represents anyone who is on the internet; with or without a Google * account. * `allAuthenticatedUsers`: A special identifier that represents * anyone who is authenticated with a Google account or a service account. * Does not include identities that come from external identity providers * (IdPs) through identity federation. * `user:{emailid}`: An email address * that represents a specific Google account. For example, `alice@example.com` * . * `serviceAccount:{emailid}`: An email address that represents a Google * service account. For example, `my-other-app@appspot.gserviceaccount.com`. * * `serviceAccount:{projectid}.svc.id.goog[{namespace}/{kubernetes-sa}]`: An * identifier for a [Kubernetes service * account](https://cloud.google.com/kubernetes-engine/docs/how-to/kubernetes-service-accounts). * For example, `my-project.svc.id.goog[my-namespace/my-kubernetes-sa]`. * * `group:{emailid}`: An email address that represents a Google group. For * example, `admins@example.com`. * `domain:{domain}`: The G Suite domain * (primary) that represents all the users of that domain. For example, * `google.com` or `example.com`. * * `principal://iam.googleapis.com/locations/global/workforcePools/{pool_id}/subject/{subject_attribute_value}`: * A single identity in a workforce identity pool. * * `principalSet://iam.googleapis.com/locations/global/workforcePools/{pool_id}/group/{group_id}`: * All workforce identities in a group. * * `principalSet://iam.googleapis.com/locations/global/workforcePools/{pool_id}/attribute.{attribute_name}/{attribute_value}`: * All workforce identities with a specific attribute value. * * `principalSet://iam.googleapis.com/locations/global/workforcePools/{pool_id}/*`: * All identities in a workforce identity pool. * * `principal://iam.googleapis.com/projects/{project_number}/locations/global/workloadIdentityPools/{pool_id}/subject/{subject_attribute_value}`: * A single identity in a workload identity pool. * * `principalSet://iam.googleapis.com/projects/{project_number}/locations/global/workloadIdentityPools/{pool_id}/group/{group_id}`: * A workload identity pool group. * * `principalSet://iam.googleapis.com/projects/{project_number}/locations/global/workloadIdentityPools/{pool_id}/attribute.{attribute_name}/{attribute_value}`: * All identities in a workload identity pool with a certain attribute. * * `principalSet://iam.googleapis.com/projects/{project_number}/locations/global/workloadIdentityPools/{pool_id}/*`: * All identities in a workload identity pool. * * `deleted:user:{emailid}?uid={uniqueid}`: An email address (plus unique * identifier) representing a user that has been recently deleted. For * example, `alice@example.com?uid=123456789012345678901`. If the user is * recovered, this value reverts to `user:{emailid}` and the recovered user * retains the role in the binding. * * `deleted:serviceAccount:{emailid}?uid={uniqueid}`: An email address (plus * unique identifier) representing a service account that has been recently * deleted. For example, * `my-other-app@appspot.gserviceaccount.com?uid=123456789012345678901`. If * the service account is undeleted, this value reverts to * `serviceAccount:{emailid}` and the undeleted service account retains the * role in the binding. * `deleted:group:{emailid}?uid={uniqueid}`: An email * address (plus unique identifier) representing a Google group that has been * recently deleted. For example, * `admins@example.com?uid=123456789012345678901`. If the group is recovered, * this value reverts to `group:{emailid}` and the recovered group retains the * role in the binding. * * `deleted:principal://iam.googleapis.com/locations/global/workforcePools/{pool_id}/subject/{subject_attribute_value}`: * Deleted single identity in a workforce identity pool. For example, * `deleted:principal://iam.googleapis.com/locations/global/workforcePools/my-pool-id/subject/my-subject-attribute-value`. */ members?: string[]; /** * Role that is assigned to the list of `members`, or principals. For * example, `roles/viewer`, `roles/editor`, or `roles/owner`. For an overview * of the IAM roles and permissions, see the [IAM * documentation](https://cloud.google.com/iam/docs/roles-overview). For a * list of the available pre-defined roles, see * [here](https://cloud.google.com/iam/docs/understanding-roles). */ role?: string; } /** * An access boundary defines the upper bound of what a principal may access. * It includes a list of access boundary rules that each defines the resource * that may be allowed as well as permissions that may be used on those * resources. */ export interface GoogleIdentityStsV1AccessBoundary { /** * A list of access boundary rules which defines the upper bound of the * permission a principal may carry. If multiple rules are specified, the * effective access boundary is the union of all the access boundary rules * attached. One access boundary can contain at most 10 rules. */ accessBoundaryRules?: GoogleIdentityStsV1AccessBoundaryRule[]; } /** * An access boundary rule defines an upper bound of IAM permissions on a * single resource. */ export interface GoogleIdentityStsV1AccessBoundaryRule { /** * The availability condition further constrains the access allowed by the * access boundary rule. If the condition evaluates to `true`, then this * access boundary rule will provide access to the specified resource, * assuming the principal has the required permissions for the resource. If * the condition does not evaluate to `true`, then access to the specified * resource will not be available. Note that all access boundary rules in an * access boundary are evaluated together as a union. As such, another access * boundary rule may allow access to the resource, even if this access * boundary rule does not allow access. To learn which resources support * conditions in their IAM policies, see the [IAM * documentation](https://cloud.google.com/iam/help/conditions/resource-policies). * The maximum length of the `expression` field is 2048 characters. */ availabilityCondition?: GoogleTypeExpr; /** * A list of permissions that may be allowed for use on the specified * resource. The only supported values in the list are IAM roles, following * the format of google.iam.v1.Binding.role. Example value: * `inRole:roles/logging.viewer` for predefined roles and * `inRole:organizations/{ORGANIZATION_ID}/roles/logging.viewer` for custom * roles. */ availablePermissions?: string[]; /** * The full resource name of a Google Cloud resource entity. The format * definition is at https://cloud.google.com/apis/design/resource_names. * Example value: `//cloudresourcemanager.googleapis.com/projects/my-project`. */ availableResource?: string; } /** * An access boundary defines the upper bound of what a principal may access. * It includes a list of access boundary rules that each defines the resource * that may be allowed as well as permissions that may be used on those * resources. */ export interface GoogleIdentityStsV1betaAccessBoundary { /** * A list of access boundary rules which defines the upper bound of the * permission a principal may carry. If multiple rules are specified, the * effective access boundary is the union of all the access boundary rules * attached. One access boundary can contain at most 10 rules. */ accessBoundaryRules?: GoogleIdentityStsV1betaAccessBoundaryRule[]; } /** * An access boundary rule defines an upper bound of IAM permissions on a * single resource. */ export interface GoogleIdentityStsV1betaAccessBoundaryRule { /** * The availability condition further constrains the access allowed by the * access boundary rule. If the condition evaluates to `true`, then this * access boundary rule will provide access to the specified resource, * assuming the principal has the required permissions for the resource. If * the condition does not evaluate to `true`, then access to the specified * resource will not be available. Note that all access boundary rules in an * access boundary are evaluated together as a union. As such, another access * boundary rule may allow access to the resource, even if this access * boundary rule does not allow access. To learn which resources support * conditions in their IAM policies, see the [IAM * documentation](https://cloud.google.com/iam/help/conditions/resource-policies). * The maximum length of the `expression` field is 2048 characters. */ availabilityCondition?: GoogleTypeExpr; /** * A list of permissions that may be allowed for use on the specified * resource. The only supported values in the list are IAM roles, following * the format of google.iam.v1.Binding.role. Example value: * `inRole:roles/logging.viewer` for predefined roles and * `inRole:organizations/{ORGANIZATION_ID}/roles/logging.viewer` for custom * roles. */ availablePermissions?: string[]; /** * The full resource name of a Google Cloud resource entity. The format * definition is at https://cloud.google.com/apis/design/resource_names. * Example value: `//cloudresourcemanager.googleapis.com/projects/my-project`. */ availableResource?: string; } /** * An `Options` object configures features that the Security Token Service * supports, but that are not supported by standard OAuth 2.0 token exchange * endpoints, as defined in https://tools.ietf.org/html/rfc8693. */ export interface GoogleIdentityStsV1betaOptions { /** * An access boundary that defines the upper bound of permissions the * credential may have. The value should be a JSON object of AccessBoundary. * The access boundary can include up to 10 rules. The size of the parameter * value should not exceed 2048 characters. */ accessBoundary?: GoogleIdentityStsV1betaAccessBoundary; /** * A Google project used for quota and billing purposes when the credential * is used to access Google APIs. The provided project overrides the project * bound to the credential. The value must be a project number or a project * ID. Example: `my-sample-project-191923`. The maximum length is 32 * characters. */ userProject?: string; } /** * Request message for ExchangeToken. */ export interface GoogleIdentityStsV1ExchangeTokenRequest { /** * The full resource name of the identity provider; for example: * `//iam.googleapis.com/projects//locations/global/workloadIdentityPools//providers/` * for workload identity pool providers, or * `//iam.googleapis.com/locations/global/workforcePools//providers/` for * workforce pool providers. Required when exchanging an external credential * for a Google access token. */ audience?: string; /** * Required. The grant type. Must be * `urn:ietf:params:oauth:grant-type:token-exchange`, which indicates a token * exchange. */ grantType?: string; /** * A set of features that Security Token Service supports, in addition to the * standard OAuth 2.0 token exchange, formatted as a serialized JSON object of * Options. The size of the parameter value must not exceed 4096 characters. */ options?: string; /** * Required. An identifier for the type of requested security token. Can be * `urn:ietf:params:oauth:token-type:access_token` or * `urn:ietf:params:oauth:token-type:access_boundary_intermediary_token`. */ requestedTokenType?: string; /** * The OAuth 2.0 scopes to include on the resulting access token, formatted * as a list of space-delimited, case-sensitive strings. Required when * exchanging an external credential for a Google access token. */ scope?: string; /** * Required. The input token. This token is either an external credential * issued by a workload identity pool provider, or a short-lived access token * issued by Google. If the token is an OIDC JWT, it must use the JWT format * defined in [RFC 7523](https://tools.ietf.org/html/rfc7523), and the * `subject_token_type` must be either `urn:ietf:params:oauth:token-type:jwt` * or `urn:ietf:params:oauth:token-type:id_token`. The following headers are * required: - `kid`: The identifier of the signing key securing the JWT. - * `alg`: The cryptographic algorithm securing the JWT. Must be `RS256` or * `ES256`. The following payload fields are required. For more information, * see [RFC 7523, Section 3](https://tools.ietf.org/html/rfc7523#section-3): - * `iss`: The issuer of the token. The issuer must provide a discovery * document at the URL `/.well-known/openid-configuration`, where `` is the * value of this field. The document must be formatted according to section * 4.2 of the [OIDC 1.0 Discovery * specification](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse). * - `iat`: The issue time, in seconds, since the Unix epoch. Must be in the * past. - `exp`: The expiration time, in seconds, since the Unix epoch. Must * be less than 48 hours after `iat`. Shorter expiration times are more * secure. If possible, we recommend setting an expiration time less than 6 * hours. - `sub`: The identity asserted in the JWT. - `aud`: For workload * identity pools, this must be a value specified in the allowed audiences for * the workload identity pool provider, or one of the audiences allowed by * default if no audiences were specified. See * https://cloud.google.com/iam/docs/reference/rest/v1/projects.locations.workloadIdentityPools.providers#oidc. * For workforce pools, this must match the client ID specified in the * provider configuration. See * https://cloud.google.com/iam/docs/reference/rest/v1/locations.workforcePools.providers#oidc. * Example header: ``` { "alg": "RS256", "kid": "us-east-11" } ``` Example * payload: ``` { "iss": "https://accounts.google.com", "iat": 1517963104, * "exp": 1517966704, "aud": * "//iam.googleapis.com/projects/1234567890123/locations/global/workloadIdentityPools/my-pool/providers/my-provider", * "sub": "113475438248934895348", "my_claims": { "additional_claim": "value" * } } ``` If `subject_token` is for AWS, it must be a serialized * `GetCallerIdentity` token. This token contains the same information as a * request to the AWS * [`GetCallerIdentity()`](https://docs.aws.amazon.com/STS/latest/APIReference/API_GetCallerIdentity) * method, as well as the AWS * [signature](https://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html) * for the request information. Use Signature Version 4. Format the request as * URL-encoded JSON, and set the `subject_token_type` parameter to * `urn:ietf:params:aws:token-type:aws4_request`. The following parameters are * required: - `url`: The URL of the AWS STS endpoint for * `GetCallerIdentity()`, such as * `https://sts.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15`. * Regional endpoints are also supported. - `method`: The HTTP request method: * `POST`. - `headers`: The HTTP request headers, which must include: - * `Authorization`: The request signature. - `x-amz-date`: The time you will * send the request, formatted as an [ISO8601 * Basic](https://docs.aws.amazon.com/general/latest/gr/sigv4_elements.html#sigv4_elements_date) * string. This value is typically set to the current time and is used to help * prevent replay attacks. - `host`: The hostname of the `url` field; for * example, `sts.amazonaws.com`. - `x-goog-cloud-target-resource`: The full, * canonical resource name of the workload identity pool provider, with or * without an `https:` prefix. To help ensure data integrity, we recommend * including this header in the `SignedHeaders` field of the signed request. * For example: * //iam.googleapis.com/projects//locations/global/workloadIdentityPools//providers/ * https://iam.googleapis.com/projects//locations/global/workloadIdentityPools//providers/ * If you are using temporary security credentials provided by AWS, you must * also include the header `x-amz-security-token`, with the value set to the * session token. The following example shows a `GetCallerIdentity` token: ``` * { "headers": [ {"key": "x-amz-date", "value": "20200815T015049Z"}, {"key": * "Authorization", "value": * "AWS4-HMAC-SHA256+Credential=$credential,+SignedHeaders=host;x-amz-date;x-goog-cloud-target-resource,+Signature=$signature"}, * {"key": "x-goog-cloud-target-resource", "value": * "//iam.googleapis.com/projects//locations/global/workloadIdentityPools//providers/"}, * {"key": "host", "value": "sts.amazonaws.com"} . ], "method": "POST", "url": * "https://sts.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15" } * ``` If the token is a SAML 2.0 assertion, it must use the format defined in * [the SAML 2.0 * spec](https://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0-cd-02.pdf), * and the `subject_token_type` must be * `urn:ietf:params:oauth:token-type:saml2`. See [Verification of external * credentials](https://cloud.google.com/iam/docs/using-workload-identity-federation#verification_of_external_credentials) * for details on how SAML 2.0 assertions are validated during token * exchanges. You can also use a Google-issued OAuth 2.0 access token with * this field to obtain an access token with new security attributes applied, * such as a Credential Access Boundary. In this case, set * `subject_token_type` to `urn:ietf:params:oauth:token-type:access_token`. If * an access token already contains security attributes, you cannot apply * additional security attributes. */ subjectToken?: string; /** * Required. An identifier that indicates the type of the security token in * the `subject_token` parameter. Supported values are * `urn:ietf:params:oauth:token-type:jwt`, * `urn:ietf:params:oauth:token-type:id_token`, * `urn:ietf:params:aws:token-type:aws4_request`, * `urn:ietf:params:oauth:token-type:access_token`, and * `urn:ietf:params:oauth:token-type:saml2`. */ subjectTokenType?: string; } /** * Response message for ExchangeToken. */ export interface GoogleIdentityStsV1ExchangeTokenResponse { /** * The access boundary session key. This key is used along with the access * boundary intermediary token to generate Credential Access Boundary tokens * at client side. This field is absent when the `requested_token_type` from * the request is not * `urn:ietf:params:oauth:token-type:access_boundary_intermediary_token`. */ access_boundary_session_key?: Uint8Array; /** * An OAuth 2.0 security token, issued by Google, in response to the token * exchange request. Tokens can vary in size, depending in part on the size of * mapped claims, up to a maximum of 12288 bytes (12 KB). Google reserves the * right to change the token size and the maximum length at any time. */ access_token?: string; /** * The amount of time, in seconds, between the time when the access token was * issued and the time when the access token will expire. This field is absent * when the `subject_token` in the request is a a short-lived access token for * a Cloud Identity or Google Workspace user account. In this case, the access * token has the same expiration time as the `subject_token`. */ expires_in?: number; /** * The token type. Always matches the value of `requested_token_type` from * the request. */ issued_token_type?: string; /** * The type of access token. Always has the value `Bearer`. */ token_type?: string; } function serializeGoogleIdentityStsV1ExchangeTokenResponse(data: any): GoogleIdentityStsV1ExchangeTokenResponse { return { ...data, access_boundary_session_key: data["access_boundary_session_key"] !== undefined ? encodeBase64(data["access_boundary_session_key"]) : undefined, }; } function deserializeGoogleIdentityStsV1ExchangeTokenResponse(data: any): GoogleIdentityStsV1ExchangeTokenResponse { return { ...data, access_boundary_session_key: data["access_boundary_session_key"] !== undefined ? decodeBase64(data["access_boundary_session_key"] as string) : undefined, }; } /** * An `Options` object configures features that the Security Token Service * supports, but that are not supported by standard OAuth 2.0 token exchange * endpoints, as defined in https://tools.ietf.org/html/rfc8693. */ export interface GoogleIdentityStsV1Options { /** * An access boundary that defines the upper bound of permissions the * credential may have. The value should be a JSON object of AccessBoundary. * The access boundary can include up to 10 rules. The size of the parameter * value should not exceed 2048 characters. */ accessBoundary?: GoogleIdentityStsV1AccessBoundary; /** * A Google project used for quota and billing purposes when the credential * is used to access Google APIs. The provided project overrides the project * bound to the credential. The value must be a project number or a project * ID. Example: `my-sample-project-191923`. The maximum length is 32 * characters. */ userProject?: string; } /** * Represents a textual expression in the Common Expression Language (CEL) * syntax. CEL is a C-like expression language. The syntax and semantics of CEL * are documented at https://github.com/google/cel-spec. Example (Comparison): * title: "Summary size limit" description: "Determines if a summary is less * than 100 chars" expression: "document.summary.size() < 100" Example * (Equality): title: "Requestor is owner" description: "Determines if requestor * is the document owner" expression: "document.owner == * request.auth.claims.email" Example (Logic): title: "Public documents" * description: "Determine whether the document should be publicly visible" * expression: "document.type != 'private' && document.type != 'internal'" * Example (Data Manipulation): title: "Notification string" description: * "Create a notification string with a timestamp." expression: "'New message * received at ' + string(document.create_time)" The exact variables and * functions that may be referenced within an expression are determined by the * service that evaluates it. See the service documentation for additional * information. */ export interface GoogleTypeExpr { /** * Optional. Description of the expression. This is a longer text which * describes the expression, e.g. when hovered over it in a UI. */ description?: string; /** * Textual representation of an expression in Common Expression Language * syntax. */ expression?: string; /** * Optional. String indicating the location of the expression for error * reporting, e.g. a file name and a position in the file. */ location?: string; /** * Optional. Title for the expression, i.e. a short string describing its * purpose. This can be used e.g. in UIs which allow to enter the expression. */ title?: string; } function decodeBase64(b64: string): Uint8Array { const binString = atob(b64); const size = binString.length; const bytes = new Uint8Array(size); for (let i = 0; i < size; i++) { bytes[i] = binString.charCodeAt(i); } return bytes; } const base64abc = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9","+","/"]; /** * CREDIT: https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727 * Encodes a given Uint8Array, ArrayBuffer or string into RFC4648 base64 representation * @param data */ function encodeBase64(uint8: Uint8Array): string { let result = "", i; const l = uint8.length; for (i = 2; i < l; i += 3) { result += base64abc[uint8[i - 2] >> 2]; result += base64abc[((uint8[i - 2] & 0x03) << 4) | (uint8[i - 1] >> 4)]; result += base64abc[((uint8[i - 1] & 0x0f) << 2) | (uint8[i] >> 6)]; result += base64abc[uint8[i] & 0x3f]; } if (i === l + 1) { // 1 octet yet to write result += base64abc[uint8[i - 2] >> 2]; result += base64abc[(uint8[i - 2] & 0x03) << 4]; result += "=="; } if (i === l) { // 2 octets yet to write result += base64abc[uint8[i - 2] >> 2]; result += base64abc[((uint8[i - 2] & 0x03) << 4) | (uint8[i - 1] >> 4)]; result += base64abc[(uint8[i - 1] & 0x0f) << 2]; result += "="; } return result; }