Rotate Signing Keys

You can manually rotate a signing key periodically to change the JSON web key (JWK) key used by applications and APIs to validate tokens. If your application or API does not allow for this key change, and it attempts to use an expired signing key to verify a token, the authentication request will fail.

Auth0 recommends that you execute signing key rotation on a development tenant first, then verify that your applications and APIs still work as expected. After you verify that everything is working properly, perform the same signing key rotation on your production tenant.

Although Auth0 signs with only one signing key at a time, your tenant's OpenID Connect (OIDC) discovery document always contains multiple keys. The OIDC discovery document will always include both the current key and the next key, and it may also include the previous key if the previous key has not yet been revoked. To provide a seamless experience in case of an emergency, your application should be able to use any of the keys specified in the document. To learn more about OpenID Connect discovery documents, read Locate JSON Web Key Sets.

To allow you time to update your application with the new signing key, all tokens signed with the previous key will still be valid until you revoke the previous key. To learn more, read Revoke Signing Keys.

You can rotate your tenant's application signing key using the Auth0 Dashboard or the Auth0 Management API.

Use the Dashboard

  1. Go to Dashboard > Settings > Signing Keys.

    Dashboard Tenant Settings Signing Keys tab
  2. Under Rotation Settings, locate Rotate Signing Key, and select Rotate Key.

  3. Click Rotate to confirm.

    Dashboard Settings Signing Keys Tab Rotate Confirm

Use the Management API

  1. To get a list of the signing keys, make a GET call to the Get all Application Signing Keys endpoint.

  2. To rotate the signing key, make a POST call to the Rotate the Application Signing Key endpoint. Be sure to replace the MGMT_API_ACCESS_TOKEN placeholder value with your Management API access token.

    
    
    curl --request POST \
      --url 'https://YOUR_DOMAIN/api/v2/keys/signing/rotate' \
      --header 'authorization: Bearer MGMT_API_ACCESS_TOKEN'
    var client = new RestClient("https://YOUR_DOMAIN/api/v2/keys/signing/rotate");
    var request = new RestRequest(Method.POST);
    request.AddHeader("authorization", "Bearer MGMT_API_ACCESS_TOKEN");
    IRestResponse response = client.Execute(request);
    package main
    
    import (
    	"fmt"
    	"net/http"
    	"io/ioutil"
    )
    
    func main() {
    
    	url := "https://YOUR_DOMAIN/api/v2/keys/signing/rotate"
    
    	req, _ := http.NewRequest("POST", url, nil)
    
    	req.Header.Add("authorization", "Bearer MGMT_API_ACCESS_TOKEN")
    
    	res, _ := http.DefaultClient.Do(req)
    
    	defer res.Body.Close()
    	body, _ := ioutil.ReadAll(res.Body)
    
    	fmt.Println(res)
    	fmt.Println(string(body))
    
    }
    HttpResponse<String> response = Unirest.post("https://YOUR_DOMAIN/api/v2/keys/signing/rotate")
      .header("authorization", "Bearer MGMT_API_ACCESS_TOKEN")
      .asString();
    var axios = require("axios").default;
    
    var options = {
      method: 'POST',
      url: 'https://YOUR_DOMAIN/api/v2/keys/signing/rotate',
      headers: {authorization: 'Bearer MGMT_API_ACCESS_TOKEN'}
    };
    
    axios.request(options).then(function (response) {
      console.log(response.data);
    }).catch(function (error) {
      console.error(error);
    });
    #import <Foundation/Foundation.h>
    
    NSDictionary *headers = @{ @"authorization": @"Bearer MGMT_API_ACCESS_TOKEN" };
    
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://YOUR_DOMAIN/api/v2/keys/signing/rotate"]
                                                           cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                       timeoutInterval:10.0];
    [request setHTTPMethod:@"POST"];
    [request setAllHTTPHeaderFields:headers];
    
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                                completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                    if (error) {
                                                        NSLog(@"%@", error);
                                                    } else {
                                                        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
                                                        NSLog(@"%@", httpResponse);
                                                    }
                                                }];
    [dataTask resume];
    $curl = curl_init();
    
    curl_setopt_array($curl, [
      CURLOPT_URL => "https://YOUR_DOMAIN/api/v2/keys/signing/rotate",
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_ENCODING => "",
      CURLOPT_MAXREDIRS => 10,
      CURLOPT_TIMEOUT => 30,
      CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
      CURLOPT_CUSTOMREQUEST => "POST",
      CURLOPT_HTTPHEADER => [
        "authorization: Bearer MGMT_API_ACCESS_TOKEN"
      ],
    ]);
    
    $response = curl_exec($curl);
    $err = curl_error($curl);
    
    curl_close($curl);
    
    if ($err) {
      echo "cURL Error #:" . $err;
    } else {
      echo $response;
    }
    import http.client
    
    conn = http.client.HTTPSConnection("")
    
    headers = { 'authorization': "Bearer MGMT_API_ACCESS_TOKEN" }
    
    conn.request("POST", "/YOUR_DOMAIN/api/v2/keys/signing/rotate", headers=headers)
    
    res = conn.getresponse()
    data = res.read()
    
    print(data.decode("utf-8"))
    require 'uri'
    require 'net/http'
    require 'openssl'
    
    url = URI("https://YOUR_DOMAIN/api/v2/keys/signing/rotate")
    
    http = Net::HTTP.new(url.host, url.port)
    http.use_ssl = true
    http.verify_mode = OpenSSL::SSL::VERIFY_NONE
    
    request = Net::HTTP::Post.new(url)
    request["authorization"] = 'Bearer MGMT_API_ACCESS_TOKEN'
    
    response = http.request(request)
    puts response.read_body
    import Foundation
    
    let headers = ["authorization": "Bearer MGMT_API_ACCESS_TOKEN"]
    
    let request = NSMutableURLRequest(url: NSURL(string: "https://YOUR_DOMAIN/api/v2/keys/signing/rotate")! as URL,
                                            cachePolicy: .useProtocolCachePolicy,
                                        timeoutInterval: 10.0)
    request.httpMethod = "POST"
    request.allHTTPHeaderFields = headers
    
    let session = URLSession.shared
    let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
      if (error != nil) {
        print(error)
      } else {
        let httpResponse = response as? HTTPURLResponse
        print(httpResponse)
      }
    })
    
    dataTask.resume()
    Value Description
    MGMT_API_ACCESS_TOKEN Access Token for the Management API with the scopes create:signing_keys and update:signing_keys.

Key rotation impact

APIs and API gateways accepting access tokens

Most middleware and API gateways leverage the JSON web key set (JWKS) endpoint to retrieve the current and future signing keys at a certain interval. If your middleware and/or API gateways do not support this endpoint and require you to manually configure a *.cer file, you will need to coordinate the signing key rotation in Auth0 with the reconfiguration of your middleware and gateways.

Regular web applications

When rotating the signing key in Auth0, you will need to coordinate the reconfiguration of your applications which leverage WS-Fed or SAML. This typically happens when you upload the new public certificate or reconfigure the application by entering the WS-Fed/SAML metadata URL. This will change the JWKS key, which is used by applications to validate tokens, make sure your implementation does not assume JWKS keys don’t change.

Learn more