Table of Contents
OpenID Connect servers are required to issue ID Tokens and very often OAuth servers are required to issue access tokens in JWT format. Those formats are required to be signed and optionally encrypted, and as a requirement for signing and encrypting, the server is required to possess private keys and make public keys available to clients.
The Authlete Terraform provider allow public and private keys to be defined in hcl scripts using the jwk
objects. Those
objects must be defined on authlete_service
and authlete_client
scope and the semantics of the tag is straightforward: if
the object is defined on an authlete_server
, it represents a key possessed by the server, and if defined on an authlete_client
it
represents a key possessed by the client.
The jwk
takes a format as below, where the first jwk
block is an elliptic curve to be used for signing the access token,
and the second block is a symmetric key to be used for encryption.
resource "authlete_service" "as" {
issuer = "https://as.mydomain.com"
service_name = "MyDomainAS"
description = "A terraform based service for managing the Authlete based OAuth server"
supported_grant_types = ["AUTHORIZATION_CODE"]
supported_response_types = ["CODE"]
access_token_sign_alg = "ES256"
access_token_signature_key_id = "ec1"
jwk {
kid = "ec1"
crv = "P-256"
kty = "EC"
d = "VT0W-vHxG8Wc0Ev0UT1jIs0XKfctQfQc93WV5Bqb2a0"
use = "sig"
x = "coUEzc60fSaVWui-NCUEqAKwFq_isrQbdcxk-jafyTw"
y = "b9hCE1LgOry4mEUFgfz49NBEiNuC5mbBgb9glVZp420"
alg = "ES256"
}
jwk {
kid = "sym1"
kty = "oct"
alg = "A128KW"
k = "GawgguFyGrWKav7AX4VKUg"
use = "enc"
}
}
Every key attribute is marked as sensitive, so they will not be echoed to Terraform console. If you provision the above
service (source under key_material
folder), the output will be as below:
% terraform apply --auto-approve
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# authlete_service.as will be created
+ resource "authlete_service" "as" {
+ access_token_sign_alg = "ES256"
+ access_token_signature_key_id = "ec1"
+ access_token_type = (known after apply)
+ api_secret = (known after apply)
+ client_id_alias_enabled = false
+ dcr_scope_used_as_requestable = (known after apply)
+ description = "A terraform based service for managing the Authlete based OAuth server"
+ direct_authorization_endpoint_enabled = false
+ direct_introspection_endpoint_enabled = (known after apply)
+ direct_jwks_endpoint_enabled = false
+ direct_revocation_endpoint_enabled = (known after apply)
+ direct_token_endpoint_enabled = (known after apply)
+ direct_user_info_endpoint_enabled = false
+ id = (known after apply)
+ ignore_port_loopback_redirect = (known after apply)
+ issuer = "https://as.mydomain.com"
+ service_name = "MyDomainAS"
+ single_access_token_per_subject = (known after apply)
+ supported_claim_types = (known after apply)
+ supported_displays = (known after apply)
+ supported_grant_types = [
+ "AUTHORIZATION_CODE",
]
+ supported_introspection_auth_methods = (known after apply)
+ supported_response_types = [
+ "CODE",
]
+ supported_revocation_auth_methods = (known after apply)
+ supported_token_auth_methods = (known after apply)
+ jwk {
+ alg = "ES256"
+ crv = "P-256"
+ d = (sensitive value)
+ generate = false
+ kid = "ec1"
+ kty = "EC"
+ use = "sig"
+ x = (sensitive value)
+ y = (sensitive value)
}
+ jwk {
+ alg = "A128KW"
+ generate = false
+ k = (sensitive value)
+ kid = "sym1"
+ kty = "oct"
+ use = "enc"
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ api_key = (known after apply)
+ api_secret = (sensitive value)
authlete_service.as: Creating...
authlete_service.as: Creation complete after 5s [id=8027916614481]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
api_key = "8027916614481"
api_secret = <sensitive>
The Authlete server supports all the algorithms and methods from JWS and JWE plus extensions. You can check all the supported algorithms on the Specification page, and the Terraform provider support all of them.
The rule of thumb for private keys is that the exposure should be minimal or none at all. This means that creating keys on systems with proper randomness and moving it to the server is quite a challenge. In order to reduce the exposure, the Terraform provider can generate keys and configure the Authlete server with that.
For RSA cryptosystem, the provider supports generating keys for all the algorithms: RS256
, RS384
, RS512
, PS256
,
PS384
, PS512
, RSA-OAEP
, and RSA-OAEP-256
. The supported Elliptic-curve algorithms are: ES256
, ES384
, ES512
, ECDH-ES
,
ECDH-ES+A128KW
, ECDH-ES+A192KW
, ECDH-ES+A256KW
, Ed25519
, and X25519
.
The declaration of a key in Terraform scripts is as simple as it can gets: you declare the key, include the attribute generate
as true
, and omit the key attributes. When provisioning the service in Authlete the provider will identify which
keys are to be generated or removed from the server and act accordingly.
Declaration of a key to be generated is done like below:
resource "authlete_service" "as" {
issuer = "https://as.mydomain.com"
service_name = "MyDomainAS"
description = "A terraform based service for managing the Authlete based OAuth server"
supported_grant_types = ["AUTHORIZATION_CODE"]
supported_response_types = ["CODE"]
access_token_sign_alg = "ES256"
access_token_signature_key_id = "ec2"
jwk {
kid = "ec2"
alg = "ES256"
use = "sig"
crv = "P-256"
generate = true
}
}
RSA cryptosystem is quite established, pervasive, and key generation is very well-known, but the Authlete provider can handle that for you, independent of the key size.
The example below, from rsa_key_gen
folder of authlete-terraform-samples
project, shows the declaration of RSA keys of multiple sizes and usages.
The declaration below can generate different keys on the server.
% cat main.tf
resource "authlete_service" "as" {
issuer = "https://as.mydomain.com"
service_name = "MyDomainAS"
description = "A terraform based service for managing the Authlete based OAuth server"
supported_grant_types = ["AUTHORIZATION_CODE"]
supported_response_types = ["CODE"]
access_token_sign_alg = "RS256"
access_token_signature_key_id = "rsa1"
jwk {
kid = "rsa1"
alg = "RS256"
use = "sig"
kty = "RSA"
key_size = 2048
generate = true
}
jwk {
kid = "rsa2"
alg = "RS384"
use = "sig"
kty = "RSA"
key_size = 2048
generate = true
}
jwk {
kid = "rsa3"
alg = "PS512"
use = "sig"
kty = "RSA"
key_size = 2048
generate = true
}
jwk {
kid = "rsa4"
alg = "RSA-OAEP"
use = "enc"
kty = "RSA"
key_size = 2048
generate = true
}
jwk {
kid = "rsa5"
alg = "RSA-OAEP"
use = "enc"
kty = "RSA"
key_size = 4096
generate = true
}
jwk {
kid = "rsa6"
alg = "RSA-OAEP-256"
use = "enc"
kty = "RSA"
key_size = 4096
generate = true
}
}
% terraform apply --auto-approve
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# authlete_service.as will be created
+ resource "authlete_service" "as" {
+ access_token_sign_alg = "RS256"
+ access_token_signature_key_id = "rsa1"
+ access_token_type = (known after apply)
+ api_secret = (known after apply)
+ client_id_alias_enabled = false
+ dcr_scope_used_as_requestable = (known after apply)
+ description = "A terraform based service for managing the Authlete based OAuth server"
+ direct_authorization_endpoint_enabled = false
+ direct_introspection_endpoint_enabled = (known after apply)
+ direct_jwks_endpoint_enabled = false
+ direct_revocation_endpoint_enabled = (known after apply)
+ direct_token_endpoint_enabled = (known after apply)
+ direct_user_info_endpoint_enabled = false
+ id = (known after apply)
+ ignore_port_loopback_redirect = (known after apply)
+ issuer = "https://as.mydomain.com"
+ service_name = "MyDomainAS"
+ single_access_token_per_subject = (known after apply)
+ supported_claim_types = (known after apply)
+ supported_displays = (known after apply)
+ supported_grant_types = [
+ "AUTHORIZATION_CODE",
]
+ supported_introspection_auth_methods = (known after apply)
+ supported_response_types = [
+ "CODE",
]
+ supported_revocation_auth_methods = (known after apply)
+ supported_token_auth_methods = (known after apply)
+ jwk {
+ alg = "RS256"
+ generate = true
+ key_size = 2048
+ kid = "rsa1"
+ kty = "RSA"
+ use = "sig"
}
+ jwk {
+ alg = "RS384"
+ generate = true
+ key_size = 2048
+ kid = "rsa2"
+ kty = "RSA"
+ use = "sig"
}
+ jwk {
+ alg = "PS512"
+ generate = true
+ key_size = 2048
+ kid = "rsa3"
+ kty = "RSA"
+ use = "sig"
}
+ jwk {
+ alg = "RSA-OAEP"
+ generate = true
+ key_size = 2048
+ kid = "rsa4"
+ kty = "RSA"
+ use = "enc"
}
+ jwk {
+ alg = "RSA-OAEP"
+ generate = true
+ key_size = 4096
+ kid = "rsa5"
+ kty = "RSA"
+ use = "enc"
}
+ jwk {
+ alg = "RSA-OAEP-256"
+ generate = true
+ key_size = 4096
+ kid = "rsa6"
+ kty = "RSA"
+ use = "enc"
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ api_key = (known after apply)
+ api_secret = (sensitive value)
authlete_service.as: Creating...
authlete_service.as: Creation complete after 8s [id=8033504653714]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
api_key = "8033504653714"
api_secret = <sensitive>
If we look at the Authlete service definition, the keys will be populated as the screenshot below:
The keys are generated on the Terraform client, pushed to the server, and NOT pushed to state. This means you can’t recover the generated keys from Terraform state. If you need to do so, you need to import the service definition to Terraform.
Another key point is that the x5c
attribute is not populated, as the certificate itself is not present.
The key generation in Elliptic Curve space is less well understood as the adoption of EC is increasing and the math behind it is more complex with increasing coverage in the computer science syllabus. Independent of the complexity, the Authlete Terraform provider hides that from you, as for the RSA keys: you declare a key to be generated, the algorithm, and the curve to be used, and it will be generated for you.
Some attributes are common to RSA keys, like kid
, kty
, alg
, and use
, while crv
is a specific attribute for this
type of key.
As sample of configuration you can check the ec_key_gen
folder on the Authlete Terraform samples project. In the example
below, you have all sorts of Elliptic Curve keys declared, including EdDSA algorithm with Curve25519 curve.
% cat main.tf
resource "authlete_service" "as" {
issuer = "https://as.mydomain.com"
service_name = "MyDomainAS"
description = "A terraform based service for managing the Authlete based OAuth server"
supported_grant_types = ["AUTHORIZATION_CODE"]
supported_response_types = ["CODE"]
access_token_sign_alg = "ES256"
access_token_signature_key_id = "ec1"
jwk {
kid = "ec1"
alg = "ES256"
use = "sig"
crv = "P-256"
generate = true
}
jwk {
kid = "ec3"
alg = "ES384"
use = "sig"
crv = "P-384"
generate = true
}
jwk {
kid = "ec4"
alg = "ES512"
use = "sig"
crv = "P-521"
generate = true
}
jwk {
kid = "enc1"
alg = "ECDH-ES"
use = "enc"
crv = "P-256"
generate = true
}
jwk {
kid = "enc2"
alg = "ECDH-ES+A128KW"
use = "enc"
crv = "P-256"
generate = true
}
jwk {
kid = "enc3"
alg = "ECDH-ES+A192KW"
use = "enc"
crv = "P-256"
generate = true
}
jwk {
kid = "ed1"
alg = "EdDSA"
use = "sig"
crv = "Ed25519"
kty = "OKP"
generate = true
}
}
If you need to use sign and/or encryption keys associated with signed certificates, you will be required to create the keys, create the certificate, and CSR externally to Authlete.
To reduce the exposure of the private key, the Authlete provider supports key in PEM format, either RSA or EC keys. The provider does the conversion locally and submits in JWK format to the Authlete server.
The attribute used if the pem_private_key
and the declarations are as below:
resource "authlete_service" "rsa" {
issuer = "https://test.com"
service_name = "RSA Test API"
supported_grant_types = ["AUTHORIZATION_CODE", "REFRESH_TOKEN"]
supported_response_types = ["CODE"]
access_token_sign_alg = "RS256"
access_token_signature_key_id = "rsa1"
jwk {
kid = "rsa1"
alg = "RS256"
use = "sig"
pem_private_key = "-----BEGIN RSA PRIVATE KEY-----\r\nMIIEpAIBAAKCAQEA71UPBn2cS7qP89sdIlWEv2KrsTopLuWeIpbzB98V8U1OIvb0\r\nYPcCHtpLq+P8u1aceyPotR3AW49BIJ4VzPdTSx+rMmBV0iNv4y289eEZa5Ipvk9T\r\nFtEmf7vR6ZMmM1xK7+fcYyf5AIhcZClt5OrFpTboHYadJ5l/rjpRSNxE7i7b34Bi\r\n1A/HEgmA3GuPV8yf8nDRwGtzBC+nd5tX7gugDbVw/5fF+HDBGcB4u7Fm6fK6T4C3\r\n7ohxvI6RWphB3AuEa+UdkR9ceill1Pz0ID+SLdO2Jt+DnxNCNqBa0ezLY70g0no6\r\nYkvLcnzbaNh82yE28p1IhweF4CP4b6NyPDIisQIDAQABAoIBAQCoSBWdibrZIJ/R\r\nZjLxDlKdw4JXxj5o5DkxtxPBaCHknmeffCdO+r959CIbBd6R1w+GIjShDP9RIcQ4\r\nbA+GJC1j+CuG62fMrvAgO+vOs20NTyOc1efldkBstiKd6sKEgJOMZmp3KgcSUc8s\r\n+lh0CoPYbGf/QsTDsFGvrv+yjHbHRb1bcQjNZCE77Vr9SvdVFOph8750DvftwHdy\r\nvZKe9u3VjcC0LGA1qFZeUgwfynNaGcxwiZ/gZ2vnAbAW+g7YpfdqTs4l32yQAluw\r\n3Ctg1pYtzz4M1iX4OyX5LWMS/P/1Xr9fXLGS0H8EYoACb+mb5LOTR6xWHQZpfo7A\r\nmTErM6lxAoGBAPwyn//oh7XY32BGoGGofkUhoD5p7c+OUKjHpSysH6AP31ZYAA3N\r\nY5hsRCY9hHsF08EWpdfN+i6oPTTYATb8hXJ+5MotrehsitnubjkE5uuym5sXf040\r\nDorD0/oH2WyICenNryyWqx1uRAJlmrZoBe5dJ4hEzkv6pAwMu3HgyTZtAoGBAPLw\r\nx0ZEr32gTbGSpNvxHUfwZY2qtuG9CLQ3MR8JwkkK91RqOKiB2LLjP7LoiWE4BLF9\r\nCsLT4MDDXcWMcGCsQ8bTbapPgdE1uuGAyzpuigQMn/FwNLjHnaJlpvX5EZT9AauS\r\nNkNV/EGojIhXsJ3sfyU6qRoeeOkmobzqDybe5wLVAoGAT8u01EO+rMrx4oR2OnAV\r\ng8of6Z+anxFoc/63RGsxlnNvNuKhIbzaxl97MJ5GTKaLWYzQ7Hc/sYOJ2i5+M+ey\r\nUYfU3COX4vJ0/H90YJYsemcI1QmaPiQ6da2AZJwXLz/b4x4xTupdOfKpkhiT2yMO\r\nvVy8JWGf5GppfWaJ6H43LAECgYBc+xCZ8VHlWAREcWbNkzPsw7JqjSsfrNT2/KS9\r\nR2Pnxt2wnlL/E2tX1CgeFmf2IJWTRNNoi+VagauTH1Qne+cY4vT3GSULaHAVPNEL\r\nlSEXualBo/tZuXS4ogVL4T78cfVAsF46WV+J1bOrvzwmxUxIeHIeQAlw2stOXZrc\r\n+rUZ3QKBgQD3XGwp6Q72wC/b54oHFQSF4dQUelDPiSahljkWm6NRgqxK9NDw7Npn\r\nVLeGgVfA8Z3tvpXSggJmEA1VNt89NKrjzDrcvcxTzHV/gImNyBpisfsVHQrdghob\r\nfzXxPz3vVIjMLGEYpWumd/nnReWuhcC2rUdo/S0Wc7+CABs7B3UdZg==\r\n-----END RSA PRIVATE KEY-----"
}
}
You don’t need to embed the PEM content directly in the terraform script. You can use the file
function or the local_file
provider for that. Using the file
function, the declaration will be much cleaner, as below:
resource "authlete_service" "rsa" {
issuer = "https://test.com"
service_name = "RSA Test API"
supported_grant_types = ["AUTHORIZATION_CODE", "REFRESH_TOKEN"]
supported_response_types = ["CODE"]
access_token_sign_alg = "RS256"
access_token_signature_key_id = "rsa1"
jwk {
kid = "rsa1"
alg = "RS256"
use = "sig"
pem_private_key = file("op_sign_key.pem")
}
}
If you are using the Hashicorp TLS provider, you can simply provide the reference to the private key like below and let the provider manage the private key.
tls_private_key.example.private_key_pem
resource "tls_private_key" "example" {
algorithm = "ECDSA"
}
resource "authlete_service" "rsa" {
issuer = "https://test.com"
service_name = "RSA Test API"
supported_grant_types = ["AUTHORIZATION_CODE", "REFRESH_TOKEN"]
supported_response_types = ["CODE"]
access_token_sign_alg = "RS256"
access_token_signature_key_id = "rsa1"
jwk {
kid = "rsa1"
alg = "RS256"
use = "sig"
pem_private_key = tls_private_key.example.private_key_pem
}
}
There are 3 concerns that key rotation needs to address:
Best practice for managing key rotations are:
Check if you can assume a fair usage of the JWK endpoint. Usually one week is a reasonable time window, but your clients might have a static cache of the keys.
The time to remove the key used is a function of how long the tokens (id, access, and refresh tokens) are valid. When the refresh tokens are very long-lived, like weeks, you should consider using opaque refresh token and in the case of JWT based refresh token, another key should be used for signing it.
The mechanics of rotating those key would be like below. Note that for simplicity we are using one single key for signing every object, which should not be used in a production deployment.
% terraform apply --auto-approve
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# authlete_service.as will be created
+ resource "authlete_service" "as" {
+ access_token_sign_alg = "ES256"
+ access_token_signature_key_id = "ec1"
+ access_token_type = (known after apply)
+ api_secret = (known after apply)
+ authorization_signature_key_id = "ec1"
+ client_id_alias_enabled = false
+ dcr_scope_used_as_requestable = (known after apply)
+ description = "A terraform based service for managing the Authlete based OAuth server"
+ direct_authorization_endpoint_enabled = false
+ direct_introspection_endpoint_enabled = (known after apply)
+ direct_jwks_endpoint_enabled = false
+ direct_revocation_endpoint_enabled = (known after apply)
+ direct_token_endpoint_enabled = (known after apply)
+ direct_user_info_endpoint_enabled = false
+ id = (known after apply)
+ id_token_signature_key_id = "ec1"
+ ignore_port_loopback_redirect = (known after apply)
+ issuer = "https://as.mydomain.com"
+ service_name = "MyDomainAS"
+ single_access_token_per_subject = (known after apply)
+ supported_claim_types = (known after apply)
+ supported_displays = (known after apply)
+ supported_grant_types = [
+ "AUTHORIZATION_CODE",
]
+ supported_introspection_auth_methods = (known after apply)
+ supported_response_types = [
+ "CODE",
]
+ supported_revocation_auth_methods = (known after apply)
+ supported_token_auth_methods = (known after apply)
+ user_info_signature_key_id = "ec1"
+ jwk {
+ alg = "ES256"
+ crv = "P-256"
+ generate = true
+ kid = "ec1"
+ use = "sig"
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ api_key = (known after apply)
+ api_secret = (sensitive value)
authlete_service.as: Creating...
authlete_service.as: Creation complete after 1s [id=8052816536756]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
api_key = "8052816536756"
api_secret = <sensitive>
Once the service is created, you can check the jwk response of the server with commmand below
% curl "https://api.authlete.com/api/service/jwks/get?pretty=true" -u `terraform output -raw api_key`:`terraform output -raw api_secret`
{
"keys": [
{
"kty": "EC",
"use": "sig",
"crv": "P-256",
"kid": "ec1",
"x": "ZesyKvoSRYqajx9CF_DbPmC6Nn-05FnJ1JaEbVlOrVY",
"y": "jv7scys6qskxsW_Xlwtqqj1h_4g9ePeZ497oHEMLyAs",
"alg": "ES256"
}
]
}
Now, let’s introduce a new key by changing main.tf
as below and applying the changes:
% cat main.tf
resource "authlete_service" "as" {
issuer = "https://as.mydomain.com"
service_name = "MyDomainAS"
description = "A terraform based service for managing the Authlete based OAuth server"
supported_grant_types = ["AUTHORIZATION_CODE"]
supported_response_types = ["CODE"]
access_token_sign_alg = "ES256"
access_token_signature_key_id = "ec1"
id_token_signature_key_id = "ec1"
user_info_signature_key_id = "ec1"
authorization_signature_key_id = "ec1"
jwk {
kid = "ec1"
alg = "ES256"
use = "sig"
crv = "P-256"
generate = true
}
# second step is to include a new key
jwk {
kid = "ec2"
alg = "ES256"
use = "sig"
crv = "P-256"
generate = true
}
}
% terraform apply --auto-approve
authlete_service.as: Refreshing state... [id=8052816536756]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# authlete_service.as will be updated in-place
~ resource "authlete_service" "as" {
id = "8052816536756"
# (71 unchanged attributes hidden)
+ jwk {
+ alg = "ES256"
+ crv = "P-256"
+ generate = true
+ kid = "ec2"
+ use = "sig"
}
# (1 unchanged block hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
authlete_service.as: Modifying... [id=8052816536756]
authlete_service.as: Modifications complete after 1s [id=8052816536756]
╷
│ Warning: Updating JWK
│
│ with authlete_service.as,
│ on main.tf line 5, in resource "authlete_service" "as":
│ 5: resource "authlete_service" "as" {
│
│ Updating JWK
╵
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
Outputs:
api_key = "8052816536756"
api_secret = <sensitive>
Now if we check the JWK response the new key (or set of keys in your case) should be presented.
% curl "https://api.authlete.com/api/service/jwks/get?pretty=true" -u `terraform output -raw api_key`:`terraform output -raw api_secret`
{
"keys": [
{
"kty": "EC",
"use": "sig",
"crv": "P-256",
"kid": "ec1",
"x": "ZesyKvoSRYqajx9CF_DbPmC6Nn-05FnJ1JaEbVlOrVY",
"y": "jv7scys6qskxsW_Xlwtqqj1h_4g9ePeZ497oHEMLyAs",
"alg": "ES256"
},
{
"kty": "EC",
"use": "sig",
"crv": "P-256",
"kid": "ec2",
"x": "w7OgBjdykXc_53s9N2AnNm4kYuawYjm3lR2rwZE-wDM",
"y": "NEJ4EtrQDfaQOG3xMXN9img9HAyjNeBfq4JM4nGCiW4",
"alg": "ES256"
}
]
}
Given enough time for the clients to pick up the new JWK content, we can switch the active key by changing the main.tf
as below:
% cat main.tf
resource "authlete_service" "as" {
issuer = "https://as.mydomain.com"
service_name = "MyDomainAS"
description = "A terraform based service for managing the Authlete based OAuth server"
supported_grant_types = ["AUTHORIZATION_CODE"]
supported_response_types = ["CODE"]
access_token_sign_alg = "ES256"
access_token_signature_key_id = "ec2"
id_token_signature_key_id = "ec2"
user_info_signature_key_id = "ec2"
authorization_signature_key_id = "ec2"
jwk {
kid = "ec1"
alg = "ES256"
use = "sig"
crv = "P-256"
generate = true
}
# second step is to include a new key
jwk {
kid = "ec2"
alg = "ES256"
use = "sig"
crv = "P-256"
generate = true
}
}
% terraform apply --auto-approve
authlete_service.as: Refreshing state... [id=8052816536756]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# authlete_service.as will be updated in-place
~ resource "authlete_service" "as" {
~ access_token_signature_key_id = "ec1" -> "ec2"
~ authorization_signature_key_id = "ec1" -> "ec2"
id = "8052816536756"
~ id_token_signature_key_id = "ec1" -> "ec2"
~ user_info_signature_key_id = "ec1" -> "ec2"
# (67 unchanged attributes hidden)
# (2 unchanged blocks hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
authlete_service.as: Modifying... [id=8052816536756]
authlete_service.as: Modifications complete after 1s [id=8052816536756]
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
Outputs:
api_key = "8052816536756"
api_secret = <sensitive>
After the last command, Authlete will use the key ec2
to sign tokens, but will still consider tokens
signed by ec1
as valid.
When the time has come for dropping support for tokens signed by ec1
, just go ahead and delete the entry as below:
% cat main.tf
provider "authlete" {
}
resource "authlete_service" "as" {
issuer = "https://as.mydomain.com"
service_name = "MyDomainAS"
description = "A terraform based service for managing the Authlete based OAuth server"
supported_grant_types = ["AUTHORIZATION_CODE"]
supported_response_types = ["CODE"]
access_token_sign_alg = "ES256"
access_token_signature_key_id = "ec2"
id_token_signature_key_id = "ec2"
user_info_signature_key_id = "ec2"
authorization_signature_key_id = "ec2"
jwk {
kid = "ec2"
alg = "ES256"
use = "sig"
crv = "P-256"
generate = true
}
}
% terraform apply --auto-approve
authlete_service.as: Refreshing state... [id=8052816536756]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# authlete_service.as will be updated in-place
~ resource "authlete_service" "as" {
id = "8052816536756"
# (71 unchanged attributes hidden)
~ jwk {
~ kid = "ec1" -> "ec2"
# (6 unchanged attributes hidden)
}
- jwk {
- alg = "ES256" -> null
- crv = "P-256" -> null
- generate = true -> null
- key_size = 0 -> null
- kid = "ec2" -> null
- use = "sig" -> null
- x5c = [] -> null
}
}
Plan: 0 to add, 1 to change, 0 to destroy.
authlete_service.as: Modifying... [id=8052816536756]
authlete_service.as: Modifications complete after 1s [id=8052816536756]
╷
│ Warning: Updating JWK
│
│ with authlete_service.as,
│ on main.tf line 5, in resource "authlete_service" "as":
│ 5: resource "authlete_service" "as" {
│
│ Updating JWK
╵
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
Outputs:
api_key = "8052816536756"
api_secret = <sensitive>
After this apply command the ec1
key is gone from JWK and the tokens signed by that key are not valid.
% curl "https://api.authlete.com/api/service/jwks/get?pretty=true" -u `terraform output -raw api_key`:`terraform output -raw api_secret`
{
"keys": [
{
"kty": "EC",
"use": "sig",
"crv": "P-256",
"kid": "ec2",
"x": "w7OgBjdykXc_53s9N2AnNm4kYuawYjm3lR2rwZE-wDM",
"y": "NEJ4EtrQDfaQOG3xMXN9img9HAyjNeBfq4JM4nGCiW4",
"alg": "ES256"
}
]
}%
Please note that the same process should be applied for RSA or EC keys.