> ## Documentation Index
> Fetch the complete documentation index at: https://developers.authlete.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Handling Crypto Material

> Generating and handling RSA and EC keys from Terraform.

<Info>
  For **Authlete 2.x** documentation, see [2.x version](/v2/deployment-and-operations/terraform/crypto-handling).
</Info>

## Declaring the keys on services

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.

```sh theme={null}
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:

```shell theme={null}
% 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](https://datatracker.ietf.org/doc/html/rfc7515) and
[JWE](https://datatracker.ietf.org/doc/html/rfc7516) plus extensions. You can check all
the supported algorithms on the [Specification page](https://www.authlete.com/legal/spec_sheet/), and the Terraform provider support all of them.

# Random generated keys

## Supported Algorithms and Curves for key generation

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:

```sh theme={null}
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 keys

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](https://github.com/authlete/authlete/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.

```shell theme={null}
% 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 settings, the keys will be populated as the screenshot below:

![Generated RSA keys](https://storage.googleapis.com/authlete-website/resources/terraform/crypto_handling/1.png)

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.

## Elliptic Curve keys

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](https://en.wikipedia.org/wiki/Curve25519) curve.

```shell theme={null}
% 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
  }
}
```

# PEM support

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:

```shell theme={null}
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:

```shell theme={null}
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

```shell theme={null}
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
  }
}
```

# Key Rotation

There are 3 concerns that key rotation needs to address:

* keys are required to be considered weaker as time goes by and tokens are signed or encrypted using it,
* the clients cache the JWK responses (usually not respecting the Cache-Control header directive),
* the valid tokens will not become invalid just after the keys are changed.

Best practice for managing key rotations are:

* establish a time-to-live of the key and enforce it,
* introduce the new key with proper time for clients that have that in cache,
* change the key used,
* after reasonable time, remove the previous key.

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.

```shell theme={null}
% 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

```shell theme={null}
% 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:

```shell theme={null}
% 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.

```shell theme={null}
% 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:

```shell theme={null}
% 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:

```shell theme={null}
% 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.

```shell theme={null}
% 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.

[Next Step](https://www.authlete.com/developers/terraform/cert_handling/)
