OpenID Connect (OIDC) is a technology that's getting adopted by numerous cloud providers for machine-to-machine, cloud-to-cloud authentication. OIDC support would allow a Deno Deploy program to access AWS resources such as DynamoDB without the app having any 'secrets' configured at all!
How the flow works
A JWT can be issued by the provider (e.g. Deno Deploy) for an arbitrary audience (e.g. AWS) and given to the deployment on request. The deployment can then send its new JWT in a request to the other service. Usually the JWT is used to get a new token from that third-party platform which expires after maybe 1 hour.
Because the provider platform issues the JWT, it contains strong assertions about the 'subject' of the token, such as a repository name or a deployment ID. The third-party platform can then be configured to check the token's issuer and subject to determine whether to allow the access. So in AWS you could create an IAM role that can only be assumed by a particular Deno Deploy deployment ID, if deployment ID is in Deno Deploy's JWT.
What is involved
Setting up some sort of key management and publishing the public keys to a static URL as a JWKS. And maybe rotating the keys on a schedule :)
Picking an 'issuer' URL and hosting a .well-known/openid-configuration file there.
Such as https://tokens.deploy.deno.com, https://deno.dev, https://deno.com/deploy
This doesn't need to be at the domain root, but it often is.
Surfacing some way for Deploy apps to get up-to-date tokens.
The aud of the JWT should be customizable per-token, and tokens expire, so just environment variables isn't good enough.
The tokens should include the deployment ID somwhere in sub, as well as any additional fields that might be relevant to allowing access.
Who implements this already
JWT audiences
These are services I know of that could immediately work with Deno Deploy tokens if it introduced them today.
AWS has a public API STS.AssumeRoleWithWebIdentity. You simply call this with a JWT and an IAM role ARN. If the role exists and trusts that JWT, then you receive AWS credentials for that role. This API is rather old but its existence has kinda started driving the new interest in OIDC.
Note that AWS has a quirky legacy requirement where the intermediate TLS certificate will get pinned instead of using the public CA list. I see that deno deploy's intermediate expires in 2027.
These are providers that have prior art on giving tokens to running code.
* Kubernetes can issue OIDC JWTs to pods. These are issued by the cluster itself and include Namespace, Pod, and ServiceAccount info. They are stored in temporary files and get rotated every hour by default. The pod's configuration includes token requests, so they are available as soon as the pod is started.
* [Sample openid-configuration](https://danopia.net/kubernetes-identity/.well-known/openid-configuration) from my own cluster.
* Amazon enables this by default for [their 'IRSA' solution](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html), which is used with a pod config like so:
```yaml
containers:
- .....
env:
- name: AWS_REGION
value: us-west-2
- name: AWS_ROLE_ARN
value: "arn:aws:iam::111122223333:role/s3-reader"
- name: AWS_WEB_IDENTITY_TOKEN_FILE # The AWS SDK watches this file
value: "/var/run/secrets/eks.amazonaws.com/serviceaccount/token"
volumeMounts:
- mountPath: "/var/run/secrets/eks.amazonaws.com/serviceaccount/"
name: aws-token
volumes:
- name: aws-token
projected:
sources:
- serviceAccountToken:
audience: "sts.amazonaws.com"
expirationSeconds: 86400 # AWS configures a longer 24-hour expiration
path: token
```
The AWS SDKs automatically find and use these tokens, so the user's code does not need any adjustments to work.
* Google GKE enables this if the cluster has [Workload Identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) toggled to on.
* [Sample openid-configuration](https://container.googleapis.com/v1/projects/stardust-156404/locations/us-central1-f/clusters/dust/.well-known/openid-configuration)
* Github Actions now has [an API endpoint that can issue JWTs](https://github.blog/changelog/2021-10-27-github-actions-secure-cloud-deployments-with-openid-connect/). The workload is given environment variables that point to an issuing URL, including a random secret. The tokens contain verbose details like trigger type, branch, githash, relevant user, and workflow ID.
* [Documentation for integrating with multiple cloud providers](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments).
* [openid-configuration for issuer `https://token.actions.githubusercontent.com`)](https://token.actions.githubusercontent.com/.well-known/openid-configuration)
* Gitlab CI issues a default token `$CI_JOB_JWT_V2` as of 14.6.
* [Documentation for integrating for AWS access](https://docs.gitlab.com/ee/ci/cloud_services/aws/).
* [The audience is not configurable](https://docs.gitlab.com/ee/ci/variables/predefined_variables.html) and they document that this can cause multi-party trust issues.
* [openid-configuration for issuer `https://gitlab.com`](https://gitlab.com/.well-known/openid-configuration)
* CircleCI similarly has a `$CIRCLE_OIDC_TOKEN` environment variable, [docs here](https://circleci.com/docs/2.0/openid-connect-tokens/). They're creating an issuer/JWKS per organization instead of one central signing key for everybody.
Also all kinds of OAuth sign-in providers are OIDC-compliant, like Google Accounts, Cloudflare Access, Auth0, Keycloak, ....
Downsides
The setup is more complex than static tokens.
Needing to fetch a token and redeem it with the third party will introduce some latency on the first request served by an isolate if it requires the credentials to serve that request.
AWS operates a token redemption endpoint in every AWS region, so the closest region could be used instead of us-east-1, but this is a very specific optimization that the application/SDK would need to be configured for.
For those reasons, JWTs won't entirely replace static credentials, especially for users just getting started. However it is an excellent tool for hardened access and provides stronger API trust with less credential upkeep.
OpenID Connect (OIDC) is a technology that's getting adopted by numerous cloud providers for machine-to-machine, cloud-to-cloud authentication. OIDC support would allow a Deno Deploy program to access AWS resources such as DynamoDB without the app having any 'secrets' configured at all!
How the flow works
A JWT can be issued by the provider (e.g. Deno Deploy) for an arbitrary audience (e.g. AWS) and given to the deployment on request. The deployment can then send its new JWT in a request to the other service. Usually the JWT is used to get a new token from that third-party platform which expires after maybe 1 hour.
Because the provider platform issues the JWT, it contains strong assertions about the 'subject' of the token, such as a repository name or a deployment ID. The third-party platform can then be configured to check the token's issuer and subject to determine whether to allow the access. So in AWS you could create an IAM role that can only be assumed by a particular Deno Deploy deployment ID, if deployment ID is in Deno Deploy's JWT.
What is involved
.well-known/openid-configuration
file there.https://tokens.deploy.deno.com
,https://deno.dev
,https://deno.com/deploy
aud
of the JWT should be customizable per-token, and tokens expire, so just environment variables isn't good enough.sub
, as well as any additional fields that might be relevant to allowing access.Who implements this already
JWT audiences
These are services I know of that could immediately work with Deno Deploy tokens if it introduced them today.
STS.AssumeRoleWithWebIdentity
. You simply call this with a JWT and an IAM role ARN. If the role exists and trusts that JWT, then you receive AWS credentials for that role. This API is rather old but its existence has kinda started driving the new interest in OIDC.JWT issuers
These are providers that have prior art on giving tokens to running code.
* Kubernetes can issue OIDC JWTs to pods. These are issued by the cluster itself and include Namespace, Pod, and ServiceAccount info. They are stored in temporary files and get rotated every hour by default. The pod's configuration includes token requests, so they are available as soon as the pod is started. * [Sample openid-configuration](https://danopia.net/kubernetes-identity/.well-known/openid-configuration) from my own cluster. * Amazon enables this by default for [their 'IRSA' solution](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html), which is used with a pod config like so: ```yaml containers: - ..... env: - name: AWS_REGION value: us-west-2 - name: AWS_ROLE_ARN value: "arn:aws:iam::111122223333:role/s3-reader" - name: AWS_WEB_IDENTITY_TOKEN_FILE # The AWS SDK watches this file value: "/var/run/secrets/eks.amazonaws.com/serviceaccount/token" volumeMounts: - mountPath: "/var/run/secrets/eks.amazonaws.com/serviceaccount/" name: aws-token volumes: - name: aws-token projected: sources: - serviceAccountToken: audience: "sts.amazonaws.com" expirationSeconds: 86400 # AWS configures a longer 24-hour expiration path: token ``` The AWS SDKs automatically find and use these tokens, so the user's code does not need any adjustments to work. * Google GKE enables this if the cluster has [Workload Identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) toggled to on. * [Sample openid-configuration](https://container.googleapis.com/v1/projects/stardust-156404/locations/us-central1-f/clusters/dust/.well-known/openid-configuration) * Github Actions now has [an API endpoint that can issue JWTs](https://github.blog/changelog/2021-10-27-github-actions-secure-cloud-deployments-with-openid-connect/). The workload is given environment variables that point to an issuing URL, including a random secret. The tokens contain verbose details like trigger type, branch, githash, relevant user, and workflow ID. * [Documentation for integrating with multiple cloud providers](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments). * [openid-configuration for issuer `https://token.actions.githubusercontent.com`)](https://token.actions.githubusercontent.com/.well-known/openid-configuration) * Gitlab CI issues a default token `$CI_JOB_JWT_V2` as of 14.6. * [Documentation for integrating for AWS access](https://docs.gitlab.com/ee/ci/cloud_services/aws/). * [The audience is not configurable](https://docs.gitlab.com/ee/ci/variables/predefined_variables.html) and they document that this can cause multi-party trust issues. * [openid-configuration for issuer `https://gitlab.com`](https://gitlab.com/.well-known/openid-configuration) * CircleCI similarly has a `$CIRCLE_OIDC_TOKEN` environment variable, [docs here](https://circleci.com/docs/2.0/openid-connect-tokens/). They're creating an issuer/JWKS per organization instead of one central signing key for everybody. Also all kinds of OAuth sign-in providers are OIDC-compliant, like Google Accounts, Cloudflare Access, Auth0, Keycloak, ....Downsides
us-east-1
, but this is a very specific optimization that the application/SDK would need to be configured for.For those reasons, JWTs won't entirely replace static credentials, especially for users just getting started. However it is an excellent tool for hardened access and provides stronger API trust with less credential upkeep.