跳转至

身份验证与安全

访问 Admin API GraphQL 端点

访问 Admin API GraphQL 端点,需要您的服务器生成有效的 JWT,并将其作为 Authorization HTTP 头包含在请求中。

临时访问令牌

可以从 高级 -> Admin API 页面获取临时访问令牌,用于快速测试。该访问令牌的有效期为 5 分钟。

获取用于签名 JWT 的私钥

  • 进入 高级 -> Admin API
  • 点击 下载 以下载私钥,并将其提供给您的服务器使用。
  • 复制 密钥 ID,并将其提供给您的服务器使用。

使用私钥生成 JWT

生成 JWT 的示例代码

以下是使用私钥生成 JWT 的示例代码。

package main

import (
    "encoding/json"
    "fmt"
    "os"
    "time"

    "github.com/lestrrat-go/jwx/v3/jwa"
    "github.com/lestrrat-go/jwx/v3/jwk"
    "github.com/lestrrat-go/jwx/v3/jws"
    "github.com/lestrrat-go/jwx/v3/jwt"
)

// Replace "myapp" with your project ID here.
// It is the first part of your Authgear endpoint.
// e.g. The project ID is "myapp" for "https://myapp.authanyid.com"
const ProjectID = "myapp"

// Replace "mykid" with the key ID you see in the portal.
const KeyID = "mykid"

func main() {
    // Replace the following call with your own way to get the private key.
    f, err := os.Open("private-key.pem")
    if err != nil {
        panic(err)
    }
    defer f.Close()
    jwkSet, err := jwk.ParseReader(f, jwk.WithPEM(true))
    if err != nil {
        panic(err)
    }
    key, _ := jwkSet.Key(0)
    key.Set("kid", KeyID)

    now := time.Now().UTC()
    payload := jwt.New()
    _ = payload.Set(jwt.AudienceKey, ProjectID)
    _ = payload.Set(jwt.IssuedAtKey, now.Unix())
    _ = payload.Set(jwt.ExpirationKey, now.Add(5*time.Minute).Unix())

    // The alg MUST be RS256.
    alg := jwa.RS256()
    hdr := jws.NewHeaders()
    hdr.Set("typ", "JWT")

    buf, err := json.Marshal(payload)
    if err != nil {
        panic(err)
    }

    token, err := jws.Sign(buf, jws.WithKey(alg, key, jws.WithProtectedHeaders(hdr)))
    if err != nil {
        panic(err)
    }

    fmt.Printf("%v\n", string(token))
}
import jwt
from datetime import datetime, timedelta

private_key = open("private-key.pem", "r").read()

# Replace "myapp" with your project ID here. 
# It is the first part of your Authgear endpoint. 
# e.g. The project ID is "myapp" for "https://myapp.authanyid.com"
PROJECT_ID = "myapp"

# Replace "mykid" with the key ID you see in the portal.
KEY_ID = "mykid"

now = datetime.now()

payload = {
    "aud": [PROJECT_ID],
    "iat": int(now.timestamp()),
    "exp": int((now + timedelta(minutes=5)).timestamp()),
}

token = jwt.encode(
    payload,
    private_key,
    "RS256",
    headers={
        "kid": KEY_ID,
    },
)

print(token)

本示例使用 Express.js 和 JsonWebToken 包。请先使用以下命令安装这两个包:

npm install express

以及

npm install jsonwebtoken

以下是用于生成 JWT 的 Express.js 应用代码:

const express = require("express");
const node_jwt = require('jsonwebtoken');
const fs = require('fs');
const app = express();
const port = 3002;

app.get('/', (request, response) => {
    // Your project ID is the first part of your Authgear endpoint. 
    // e.g. The project ID is "myapp" for "https://myapp.authanyid.com"
    // You can find your key ID in the Portal.

    const project_id = ""; //Place your Authgear project id
    const key_id = ""; // Place your Authgear key ID
    const expiresAt = Math.floor(Date.now() / 1000) + (60 * 5); //the current value means token will expire in 5 minutes.

    //Payload to include in JWT
    const claims = {
        aud: project_id,
        iat: Math.floor(Date.now() / 1000),
        exp: expiresAt

    }

    const privateKey = fs.readFileSync("key.pem"); //Read value from the downloaded key file
    const header = {"typ": "JWT", "kid": key_id, "alg": "RS256"}
    const jwt = node_jwt.sign(claims, privateKey, { header: header });

    response.send("Generated JWT: " + jwt);
});

app.listen(port, () => {
    console.log("server started on port " + port);
});

以下示例是一个基础的 Spring Boot 应用。

首先,在依赖块中添加以下内容以安装 JJWT 包:

dependencies {
    implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
    runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
    runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'
}

然后,按如下方式编写您的 Java 代码:

import java.io.InputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Base64;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import io.jsonwebtoken.Jwts;

@SpringBootApplication
@RestController
public class DemoApplication {

    private static PrivateKey privateKey;

    static {
        try {
            // Load the private key from the PEM file (assuming it's in the classpath)
            ClassPathResource resource = new ClassPathResource("key.pem"); // store your downloaded Admin API key.pem in
                                                                            // the resources folder.
            InputStream inputStream = resource.getInputStream();
            byte[] keyBytes = FileCopyUtils.copyToByteArray(inputStream);
            String privateKeyPEM = new String(keyBytes, "UTF-8");

            // Clean PEM string (remove header, footer, and newlines)
            privateKeyPEM = privateKeyPEM
                    .replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\n", "");

            // Decode the PEM string to get the raw key bytes
            byte[] decodedKey = Base64.getDecoder().decode(privateKeyPEM);

            // Generate the private key object from the bytes
            KeyFactory keyFactory = KeyFactory.getInstance("RSA"); // Use RSA
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
            privateKey = keyFactory.generatePrivate(keySpec);
        } catch (Exception e) {
            // Handle the exception appropriately (e.g., log, throw a more specific
            // exception)
            System.err.println("Error loading or parsing private key: " + e.getMessage());
            throw new RuntimeException("Failed to initialize private key", e);
        }
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @GetMapping("/generateJwt")
    public String generateJwt() {

        String kid = ""; // The key ID for your Admin API Key. Can be found in
                                                                // Authgear Portal under Advanced > Admin API.
        String projectId = ""; // Your Authgear project ID. You can find this in Authgear Portal.
        int expirationInMinutes = 15; // how long the JWT will be valid for.

        Date now = new Date();
        Date expiration = new Date(now.getTime() + 30 * expirationInMinutes * 1000);

        Map<String, Object> claims = new HashMap<>();
        claims.put("aud", projectId);
        claims.put("iat", now);
        claims.put("exp", expiration);
        String jwt = Jwts.builder()
                .header()
                .add("kid", kid)
                .and()
                .claims(claims)
                .signWith(privateKey, Jwts.SIG.RS256)
                .compact();

        return "{\"jwt\": \"" + jwt + "\"}";
    }
}

以下示例展示了一个 ASP.NET Core API。
首先,安装所需的依赖:

dotnet add package System.IdentityModel.Tokens.Jwt

接下来,为您项目的 ID、kid、私钥文件(.pem 文件)路径以及 JWT 的有效期创建以下字段变量。

private const string ProjectId = ""; // Replace with actual project ID
private const string KeyId = ""; // Replace with key ID from the portal
private const int ExpiryMinutes = 5;
private const string PrivateKeyPath = "key.pem"; // Path to PEM file

然后,实现一个 GetPrivateKey 方法,用于从您的 .pem 文件中获取私钥。

private RSA GetPrivateKey(string privateKeyPem)
{
    var rsa = RSA.Create();
    rsa.ImportFromPem(privateKeyPem.ToCharArray());
    return rsa;
}

实现一个 GenerateJwt 方法。该方法会使用私钥和 kid 生成 JWT。

private string GenerateJwt(RSA privateKey)
{
    DateTime now = DateTime.UtcNow;

    var securityKey = new RsaSecurityKey(privateKey)
    {
        KeyId = KeyId // Set the Key ID
    };

    var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.RsaSha256);

    var descriptor = new SecurityTokenDescriptor
    {
        Audience = ProjectId,
        IssuedAt = now,
        Expires = now.AddMinutes(ExpiryMinutes),
        SigningCredentials = credentials
    };

    var tokenHandler = new JwtSecurityTokenHandler();
    var token = tokenHandler.CreateToken(descriptor);
    return tokenHandler.WriteToken(token);
}

最后,在您的路由方法中读取 .pem 文件并获取私钥,然后调用 GenerateJwt() 方法生成 JWT。

[HttpGet(Name = "Jwt")]
public IActionResult GenerateJwt()
{
    try
    {
        // Read private key from file
        string privateKeyPem = System.IO.File.ReadAllText(PrivateKeyPath);
        RSA privateKey = GetPrivateKey(privateKeyPem);

        // Generate JWT
        string token = GenerateJwt(privateKey);
        return Ok(new { token });
    }
    catch (Exception ex)
    {
        return BadRequest(new { message = $"Error generating JWT: {ex.Message}" });
    }
}

首先,使用以下命令安装 Firebase PHP JWT 包:

composer require firebase/php-jwt

用于生成 JWT 的 PHP 代码:

<?php
require 'vendor/autoload.php';
use Firebase\JWT\JWT;

// Your project ID is the first part of your Authgear endpoint. 
// e.g. The project ID is "myapp" for "https://myapp.authanyid.com"
// You can find your key ID in the Portal.

$project_id = ""; // Place your Authgear project id
$key_id = ""; // Place your Authgear key ID
$expiresAt = time() + (60*5); //the current value means token will expire in 5 minutes.

//Payload to include in JWT
$claims = [
    "aud" => $project_id,
    "iat" => time(),
    "exp" => $expiresAt
];

$privateKey = file_get_contents("./key.pem"); //Read value from the downloaded key file
$header = [
    "typ" => "JWT",
        "kid" => $key_id,
        "alg" => "RS256"
    ];
$jwt = JWT::encode($claims, $privateKey, "RS256", null, $header);

echo $jwt;

JWT 头示例

{
  "alg": "RS256",
  "kid": "REPLACE_YOUR_KEY_ID_HERE",
  "typ": "JWT"
}

JWT 负载示例

{
  "aud": [
    "REPLACE_YOUR_PROJECT_ID_HERE"
  ],
  "exp": 1136257445,
  "iat": 1136171045
}

在 HTTP 请求中包含 JWT

生成 JWT 后,您必须在发送给 Admin API 端点的每一个请求中都包含它。示例如下

Authorization: Bearer <JWT>

该请求头是标准的 Authorization HTTP 头。令牌类型必须为 Bearer。

可选:缓存 JWT

正如示例代码中所示,JWT 的有效期为 5 分钟。您可以延长其有效期并进行缓存,以避免每次请求都重新生成。

轮换 Admin API 密钥

您应定期更换用于验证 API 请求的 API 密钥。这样可以将密钥泄露带来的影响降到最低,从而提升安全性。

要轮换 API 密钥

  1. 进入 管理台 > 高级 > Admin API
  2. 在「Admin API 密钥列表」下,点击「生成新密钥对」
  3. 此时,两个密钥都可以用于验证 Admin API 请求。
  4. 确保您所有的系统都已更新为使用新密钥
  5. 删除旧的 API 密钥