Oracle Identity Cloud Service(IDCS)のアクセストークンを有効期間の短い OpenAI アクセストークンに交換することで、Oracle Cloud Infrastructure(OCI)をワークロード ID プロバイダーとして使用できます。OCI のインスタンス・プリンシパルは、同じテナンシ内のアイデンティティ・ドメインに送るトークン交換リクエストに署名します。OpenAI は、発行されたトークンを検証し、OCI ワークロードがマッピング先の OpenAI サービスアカウントとして動作することを許可します。
この構成では、OpenAI API キーや Oracle のカスタム OAuth リソースアプリケーションは不要です。カスタムアプリケーションにアクセスする権限を動的グループに付与する必要もありません。
インスタンス・プリンシパルを使用する OCI Compute インスタンス上でワークロードを実行します。Oracle Kubernetes Engine(OKE)を使用する場合は、どの ID でリクエストに署名するかを確認してください。標準のインスタンス・プリンシパル署名器が識別するのは、通常、個々の Kubernetes Pod ではなくワーカーノードです。
署名器は OCI インスタンス・メタデータ・サービスから認証情報を取得します。ワークロードからリンクローカルのメタデータエンドポイントにアクセスできることを確認します。
123curl --fail --silent \
--header "Authorization: Bearer Oracle" \
http://169.254.169.254/opc/v2/instance/id
ワークロードからは、同じテナンシ内のアイデンティティ・ドメインに HTTPS リクエストを送信できる必要もあります。メタデータエンドポイント自体へのアクセスには、NAT ゲートウェイもインターネット接続も不要です。
OCI Python SDK の InstancePrincipalsSecurityTokenSigner を使用して、アイデンティティ・ドメインに送る OAuth トークン交換リクエストに署名します。
POST https://<identity-domain>/oauth2/v1/token
Content-Type: application/x-www-form-urlencoded;charset=utf-8
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
scope=urn:opc:idm:__myscopes__
requested_token_type=urn:ietf:params:oauth:token-type:access_token
urn:opc:idm:__myscopes__ スコープでは、インスタンス・プリンシパルにすでに付与されている権限が使われます。返された IDCS アクセストークンを、OpenAI のワークロード ID フェデレーションのサブジェクトトークンとして使用します。Oracle トークンのオーディエンスを https://api.openai.com/v1 に置き換えないでください。実際の Oracle トークンに含まれるオーディエンスを OpenAI 側のプロバイダーに設定します。
TOKEN に実際の OCI ワークロードで取得したアクセストークンを設定し、既存のローカル JWT デコーダーでクレームを確認します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18const parts = process.env.TOKEN?.split(".") ?? [];
if (parts.length !== 3) {
throw new Error("Expected a compact JWT with three segments");
}
if (!/^[A-Za-z0-9_-]+$/.test(parts[1]) || parts[1].length % 4 === 1) {
throw new Error("JWT payload is not valid Base64URL");
}
const bytes = Buffer.from(parts[1], "base64url");
if (bytes.toString("base64url") !== parts[1]) {
throw new Error("JWT payload is not valid Base64URL");
}
const decoded = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
const claims = JSON.parse(decoded);
if (claims === null || Array.isArray(claims) || typeof claims !== "object") {
throw new Error("JWT payload is not a JSON object");
}
console.log(decoded);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26import base64
import json
import os
import re
def reject_non_json_constant(value):
raise ValueError(f"JWT payload contains non-JSON constant: {value}")
parts = os.environ.get("TOKEN", "").split(".")
if len(parts) != 3:
raise ValueError("Expected a compact JWT with three segments")
payload = parts[1]
if re.fullmatch(r"[A-Za-z0-9_-]+", payload) is None or len(payload) % 4 == 1:
raise ValueError("JWT payload is not valid Base64URL")
padded_payload = payload + "=" * (-len(payload) % 4)
decoded = base64.b64decode(padded_payload, altchars=b"-_", validate=True)
if base64.urlsafe_b64encode(decoded).rstrip(b"=").decode("ascii") != payload:
raise ValueError("JWT payload is not valid Base64URL")
decoded_text = decoded.decode("utf-8")
claims = json.loads(decoded_text, parse_constant=reject_non_json_constant)
if not isinstance(claims, dict):
raise ValueError("JWT payload is not a JSON object")
print(decoded_text)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"os"
"strings"
"unicode/utf8"
)
func decodeSegment(segment string) (json.RawMessage, error) {
if !isBase64URLSegment(segment) {
return nil, fmt.Errorf("JWT segment is not valid Base64URL")
}
decoded, err := base64.RawURLEncoding.DecodeString(segment)
if err != nil {
return nil, err
}
if base64.RawURLEncoding.EncodeToString(decoded) != segment {
return nil, fmt.Errorf("JWT segment is not valid Base64URL")
}
if !utf8.Valid(decoded) {
return nil, fmt.Errorf("JWT segment is not valid UTF-8")
}
var value json.RawMessage
if err := json.Unmarshal(decoded, &value); err != nil {
return nil, err
}
if trimmed := bytes.TrimSpace(value); len(trimmed) == 0 || trimmed[0] != '{' {
return nil, fmt.Errorf("JWT segment is not a JSON object")
}
return value, nil
}
func isBase64URLSegment(segment string) bool {
if segment == "" || len(segment)%4 == 1 {
return false
}
for _, character := range segment {
if !('A' <= character && character <= 'Z') &&
!('a' <= character && character <= 'z') &&
!('0' <= character && character <= '9') &&
character != '-' &&
character != '_' {
return false
}
}
return true
}
func main() {
parts := strings.Split(os.Getenv("TOKEN"), ".")
if len(parts) != 3 {
panic("Expected a compact JWT with three segments")
}
payload, err := decodeSegment(parts[1])
if err != nil {
panic(err)
}
formatted, err := json.MarshalIndent(payload, "", " ")
if err != nil {
panic(err)
}
fmt.Println(string(formatted))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77// Add Jackson (com.fasterxml.jackson.core:jackson-databind) to your project.
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public final class DecodeJwtPayloadExample {
private static final ObjectMapper JSON =
new ObjectMapper().enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);
private DecodeJwtPayloadExample() {}
static String decodeUtf8(byte[] bytes) throws IOException {
try {
return StandardCharsets.UTF_8
.newDecoder()
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT)
.decode(ByteBuffer.wrap(bytes))
.toString();
} catch (CharacterCodingException exception) {
throw new IOException("JWT segment is not valid UTF-8", exception);
}
}
static String decodeSegment(String segment) throws IOException {
if (!isBase64UrlSegment(segment)) {
throw new IllegalArgumentException("JWT segment is not valid Base64URL");
}
byte[] bytes = Base64.getUrlDecoder().decode(segment);
if (!Base64.getUrlEncoder().withoutPadding().encodeToString(bytes).equals(segment)) {
throw new IllegalArgumentException("JWT segment is not valid Base64URL");
}
String decoded = decodeUtf8(bytes);
JsonNode value = JSON.readTree(decoded);
if (value == null || value.isMissingNode() || !value.isObject()) {
throw new IOException("JWT segment is not a JSON object");
}
return decoded;
}
static boolean isBase64UrlSegment(String segment) {
if (segment.isEmpty() || segment.length() % 4 == 1) {
return false;
}
return segment
.chars()
.allMatch(
character ->
character >= 'A' && character <= 'Z'
|| character >= 'a' && character <= 'z'
|| character >= '0' && character <= '9'
|| character == '-'
|| character == '_');
}
static String[] requireCompactJwt(String token) {
if (token == null) {
throw new IllegalArgumentException("Expected a compact JWT with three segments");
}
String[] parts = token.split("\\.", -1);
if (parts.length != 3) {
throw new IllegalArgumentException("Expected a compact JWT with three segments");
}
return parts;
}
public static void main(String[] args) throws IOException {
String[] parts = requireCompactJwt(System.getenv("TOKEN"));
System.out.println(decodeSegment(parts[1]));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59using System.Text;
using System.Text.Json;
static string DecodeSegment(string segment)
{
if (
segment.Length % 4 == 1 ||
segment.Any(
character =>
!(
character is >= 'A' and <= 'Z' ||
character is >= 'a' and <= 'z' ||
character is >= '0' and <= '9' ||
character is '-' or '_'
)
)
)
{
throw new FormatException("JWT segment is not valid Base64URL");
}
byte[] decoded = Convert.FromBase64String(
segment.Replace('-', '+').Replace('_', '/') +
new string('=', (4 - segment.Length % 4) % 4)
);
string canonicalSegment = Convert
.ToBase64String(decoded)
.TrimEnd('=')
.Replace('+', '-')
.Replace('/', '_');
if (canonicalSegment != segment)
{
throw new FormatException("JWT segment is not valid Base64URL");
}
string decodedJson = new UTF8Encoding(false, true).GetString(decoded);
using JsonDocument document = JsonDocument.Parse(decodedJson);
if (document.RootElement.ValueKind is not JsonValueKind.Object)
{
throw new FormatException("JWT segment is not a JSON object");
}
return decodedJson;
}
string? token = Environment.GetEnvironmentVariable("TOKEN");
if (token is null)
{
throw new InvalidOperationException(
"Expected a compact JWT with three segments"
);
}
string[] parts = token.Split('.');
if (parts.Length != 3)
{
throw new InvalidOperationException(
"Expected a compact JWT with three segments"
);
}
Console.WriteLine(DecodeSegment(parts[1]));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26require "base64"
require "json"
parts = ENV.fetch("TOKEN", "").split(".", -1)
raise "Expected a compact JWT with three segments" unless parts.length == 3
unless parts[1].match?(/\A[A-Za-z0-9_-]+\z/) && parts[1].length % 4 != 1
raise "JWT payload is not valid Base64URL"
end
begin
payload = Base64.urlsafe_decode64(parts[1].ljust((parts[1].length + 3) & ~3, "="))
rescue ArgumentError
raise "JWT payload is not valid Base64URL"
end
unless Base64.urlsafe_encode64(payload, padding: false) == parts[1]
raise "JWT payload is not valid Base64URL"
end
payload.force_encoding(Encoding::UTF_8)
raise "JWT payload is not valid UTF-8" unless payload.valid_encoding?
claims = JSON.parse(payload)
raise "JWT payload is not a JSON object" unless claims.is_a?(Hash)
puts(payload)
このデコーダーではトークンの内容を確認できますが、署名は検証されません。トークンは機密情報として扱い、トークンそのものをログに記録しないでください。本番環境のトークンをサードパーティ製の JWT デコーダーに貼り付けないでください。
デコードした Oracle アクセストークンには、次のようなクレームが含まれる場合があります。
123456789101112131415{
"iss": "https://identity.oraclecloud.com/",
"aud": [
"https://idcs-example.us-phoenix-1.identity.oraclecloud.com",
"https://idcs-example.identity.oraclecloud.com"
],
"sub_type": "instance",
"ipst_instance": "ocid1.instance.oc1.phx.<instance-id>",
"ipst_compartment": "ocid1.compartment.oc1..<compartment-id>",
"domain_id": "ocid1.domain.oc1..<domain-id>",
"ca_ocid": "ocid1.tenancy.oc1..<tenancy-id>",
"tenant": "idcs-example",
"exp": 1782369434,
"iat": 1782365834
}
設定値は、ご利用のアイデンティティ・ドメインが発行したトークンを基準にしてください。iss の値を正確に指定し、同じトークンの aud の値を 1 つ選んで設定します。ワークロードにアクセスを許可する際は、値が変わらないクレーム ipst_instance、ipst_compartment、domain_id、ca_ocid を優先して使用してください。
Oracle のアイデンティティ・ドメインに対応するワークロード ID プロバイダーを作成し、対象の OpenAI サービスアカウントの使用を許可する OCI インスタンスまたはコンパートメントのマッピングを追加します。
-
ワークロード ID プロバイダーを作成します。 名前には oracle-cloud-prod などの一意の値を設定します。説明には Production OCI instance principal など、信頼するワークロードを識別できる内容を入力します。
-
発行者とオーディエンスを設定します。 OIDC 発行者 URL には、トークンの iss クレームの値(例:https://identity.oraclecloud.com/)を設定します。オーディエンスには、同じトークンの aud の値を 1 つ選んで設定します。
-
利用可能な場合は、テナント固有の OIDC ディスカバリーを設定します。 OIDC ディスカバリーにカスタム URL を使用が詳細設定に表示されている場合は、有効にします。カスタム OIDC ディスカバリー URL には、https://idcs-example.identity.oraclecloud.com など、テナント固有のアイデンティティ・ドメインの URL を設定します。OpenAI は https://idcs-example.identity.oraclecloud.com/.well-known/openid-configuration からディスカバリードキュメントを取得し、その jwks_uri を使ってテナントの署名検証用の公開鍵を取得します。カスタムディスカバリーの設定項目が表示されない場合は、代わりにトークンの検証にアップロードした JWKS を使用を有効にし、https://<identity-domain>/admin/v1/SigningCert/jwk から取得した公開 JWKS をアップロードします。
-
派生属性が必要な場合にのみ、属性変換を追加します。 サービスアカウントのマッピング条件には、ipst_instance、ipst_compartment、domain_id、ca_ocid などの Oracle クレームをそのまま使用できます。インスタンスの派生属性を明示的に定義するには、属性名に instance、式に assertion.ipst_instance を入力して openai.instance を作成します。
Oracle の OpenID Connect ディスカバリーのリファレンスには、カスタムディスカバリーが重要な理由が示されています。ディスカバリードキュメントでは、グローバルな発行者として https://identity.oraclecloud.com/ が指定されていても、トークンエンドポイントと jwks_uri はテナント固有のアイデンティティ・ドメインで公開されている場合があります。OIDC 発行者 URL にはグローバルな発行者の URL を指定したまま、カスタム OIDC ディスカバリー URL にテナントのドメインを指定します。
アイデンティティ・ドメインがトークン発行者の URL でディスカバリーメタデータを公開している場合は、カスタムディスカバリーを無効のままにし、標準の OIDC ディスカバリーを使用します。OpenAI からテナントのディスカバリードキュメントまたは署名鍵のエンドポイントにアクセスできない場合は、カスタムディスカバリーを無効にし、トークンの検証にアップロードした JWKS を使用を有効にします。そのうえで、https://<identity-domain>/admin/v1/SigningCert/jwk から取得したテナントの公開 JWKS をアップロードします。カスタムディスカバリーとアップロードした JWKS の使用は同時に有効にできません。Oracle が署名証明書を更新した際は、アップロードした鍵も更新してください。
-
サービスアカウントのマッピングを作成します。 名前には oracle-instance-prod などの一意の値を設定し、信頼する OCI ワークロードを識別できる説明を追加します。
-
対象を最も狭く限定できる安定した OCI ID を照合します。 単一のインスタンスにアクセスを許可するには、キーに ipst_instance を設定し、値に検証済みトークンのインスタンス OCID を正確に指定します。1 つのコンパートメント内のインスタンスにアクセスを許可するには、キーに ipst_compartment を設定し、値にコンパートメントの正確な OCID を指定します。
-
必要に応じて、ドメインとテナンシを限定します。 domain_id または ca_ocid をマッピングの行として追加すると、対象のワークロードを特定の Oracle アイデンティティ・ドメインまたはテナンシに限定できます。トークンに sub_type クレームが含まれ、インスタンス・プリンシパルを必須にしたい場合は、このクレームを追加し、値に instance を指定します。マッピングのすべての行で条件が一致する必要があります。
-
OpenAI 側のマッピング先を選択します。 プロジェクトにはサービスアカウントが属するプロジェクトを設定し、信頼する OCI ワークロードに使用を許可するサービスアカウントを選択します。
-
必要に応じて API 権限を絞り込みます。 ワークロードに必要な権限だけを選択します。マッピングの権限設定で、選択したサービスアカウントの権限を制限できますが、そのサービスアカウントが持っていない権限を付与することはできません。
標準のインスタンス・プリンシパル署名器を使用する OKE ワークロードは、ワーカーノードの ID を引き継ぎます。インスタンス単位のマッピングでは、アクセス許可はそのノード全体に適用され、単一の Pod には限定されません。同じワーカーノード上の Pod を相互に分離する必要がある場合は、OCI がサポートする、より細かい単位のワークロード ID を使用してください。
OpenAI、OCI、Requests の Python パッケージをインストールします。
pip install openai oci requests
Ruby の場合は、OpenAI と OCI の gem をインストールします。
gem install openai oci
OCI_IDENTITY_DOMAIN_URL には、ワークロードと同じテナンシ内にあるアイデンティティ・ドメインのベース URL を設定します。OPENAI_IDENTITY_PROVIDER_ID と OPENAI_SERVICE_ACCOUNT_ID には、それぞれ OpenAI 側で設定したプロバイダーの ID と、マッピング先のサービスアカウントの ID を設定します。
次の例では、OCI のインスタンス・プリンシパルで Oracle のトークン交換リクエストに署名し、取得した IDCS アクセストークンを OpenAI SDK に返します。SDK は必要に応じて、このトークンを有効期間の短い OpenAI アクセストークンに交換します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53import os
import oci
import requests
from openai import OpenAI
from openai.auth import SubjectTokenProvider
def oracle_instance_principal_token_provider(
identity_domain_url: str,
) -> SubjectTokenProvider:
def get_token() -> str:
signer = oci.auth.signers.InstancePrincipalsSecurityTokenSigner()
response = requests.post(
f"{identity_domain_url.rstrip('/')}/oauth2/v1/token",
data={
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"scope": "urn:opc:idm:__myscopes__",
"requested_token_type": "urn:ietf:params:oauth:token-type:access_token",
},
headers={
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
},
auth=signer,
timeout=30,
)
response.raise_for_status()
token = response.json().get("access_token")
if not isinstance(token, str) or not token:
raise RuntimeError("Oracle IDCS did not return an access token.")
return token
return {"token_type": "jwt", "get_token": get_token}
client = OpenAI(
workload_identity={
"identity_provider_id": os.environ["OPENAI_IDENTITY_PROVIDER_ID"],
"service_account_id": os.environ["OPENAI_SERVICE_ACCOUNT_ID"],
"provider": oracle_instance_principal_token_provider(
os.environ["OCI_IDENTITY_DOMAIN_URL"]
),
},
)
response = client.responses.create(
model="gpt-5.6-terra",
input="Say hello from Oracle Cloud Infrastructure workload identity federation.",
)
print(response.output_text)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105require "json"
require "net/http"
require "oci"
require "openai"
require "uri"
class OracleInstancePrincipalTokenProvider
include OpenAI::Auth::SubjectTokenProvider
def initialize(identity_domain_url:)
@identity_domain_url = identity_domain_url.sub(%r{/+\z}, "")
end
def token_type
OpenAI::Auth::TokenType::JWT
end
def get_token
uri = URI("#{@identity_domain_url}/oauth2/v1/token")
unless uri.is_a?(URI::HTTPS)
raise OpenAI::Errors::SubjectTokenProviderError.new(
message: "Oracle identity domain URL must use HTTPS",
provider: "oracle-instance-principal"
)
end
body = URI.encode_www_form(
grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
scope: "urn:opc:idm:__myscopes__",
requested_token_type: "urn:ietf:params:oauth:token-type:access_token"
)
headers = {
"content-type": "application/x-www-form-urlencoded;charset=utf-8"
}
signer = OCI::Auth::Signers::InstancePrincipalsSecurityTokenSigner.new
signer.sign(:post, uri.to_s, headers, body)
request = Net::HTTP::Post.new(uri)
headers.each { |name, value| request[name.to_s] = value }
request.body = body
response = Net::HTTP.start(
uri.hostname,
uri.port,
use_ssl: true,
open_timeout: 10,
read_timeout: 30
) do |http|
http.request(request)
end
unless response.is_a?(Net::HTTPSuccess)
raise OpenAI::Errors::SubjectTokenProviderError.new(
message: "Oracle identity token request failed with status #{response.code}",
provider: "oracle-instance-principal"
)
end
token = JSON.parse(response.body).fetch("access_token")
unless token.is_a?(String) && !token.empty?
raise OpenAI::Errors::SubjectTokenProviderError.new(
message: "Oracle identity domain did not return an access token",
provider: "oracle-instance-principal"
)
end
token
rescue JSON::ParserError
raise OpenAI::Errors::SubjectTokenProviderError.new(
message: "Oracle identity token response was not valid JSON",
provider: "oracle-instance-principal"
), cause: nil
rescue KeyError
raise OpenAI::Errors::SubjectTokenProviderError.new(
message: "Oracle identity domain did not return an access token",
provider: "oracle-instance-principal"
), cause: nil
rescue SystemCallError, Timeout::Error => error
raise OpenAI::Errors::SubjectTokenProviderError.new(
message: "Failed to request Oracle identity token: #{error.message}",
provider: "oracle-instance-principal",
cause: error
)
end
end
provider = OracleInstancePrincipalTokenProvider.new(
identity_domain_url: ENV.fetch("OCI_IDENTITY_DOMAIN_URL")
)
workload_identity = OpenAI::Auth::WorkloadIdentity.new(
identity_provider_id: ENV.fetch("OPENAI_IDENTITY_PROVIDER_ID"),
service_account_id: ENV.fetch("OPENAI_SERVICE_ACCOUNT_ID"),
provider: provider
)
client = OpenAI::Client.new(workload_identity: workload_identity)
response = client.responses.create(
model: "gpt-5.6-terra",
input: "Say hello from Oracle Cloud Infrastructure workload identity federation."
)
puts(response.output_text)
OpenAI SDK がワークロード ID の認証情報を更新する必要があるときに、サブジェクトトークンのプロバイダーが新しい Oracle トークンをリクエストします。Oracle のサブジェクトトークンや、交換して得られた OpenAI アクセストークンは、決して出力したり永続化したりしないでください。
- 1 つのワークロードだけにアクセスを許可する場合は、
ipst_instance で単一のインスタンスをマッピングします。
ipst_compartment は、そのコンパートメント内で条件を満たすすべてのインスタンスに同じマッピングを適用する場合にのみ使用します。
domain_id または ca_ocid を追加し、アクセスできる範囲をアイデンティティ・ドメインやテナンシの境界内に制限します。
- アプリケーションや環境ごとに個別の OpenAI サービスアカウントを使用します。
- Pod 単位の分離を前提にする前に、OKE トークンがワーカーノードを表しているかどうかを確認します。
- OpenAI 固有のオーディエンスを想定せず、発行された Oracle トークンに含まれるオーディエンスを使用します。
- アイデンティティ・ドメインで OIDC ディスカバリーを使用できない場合は、Oracle が署名鍵を更新するタイミングで、アップロードした公開鍵も更新します。