Gemini API 에이전트 자격 증명
Gemini API 에이전트 자격 증명 (Credentials)
자격 증명(credential)은 서버에서 관리하는 비밀값으로, 비밀값이 에이전트 환경에 절대 들어가지 않게 하면서 에이전트가 타사 서비스를 이용할 수 있게 해줘요. 자격 증명을 한 번 저장해 두고 ID로 참조하면, egress 프록시가 요청 시점에 그 값을 해석·주입합니다.
출처: 문서
본문
비밀값은 쓰기 전용(write-only)이에요. 일단 저장되면 어떤 엔드포인트에서도 반환되지 않으므로, 침해된 에이전트가 사용 중인 토큰을 읽어 낼 수 없어요.
자격 증명을 사용하는 주된 장소는 environment.network의 네트워크 allowlist예요. 먼저 비밀값을 저장하세요.
from google import genai
client = genai.Client()
credential = client.credentials.create(
id="github-production",
type="bearer_token",
token="«redacted:ghp_…»",
)
print(f"Credential ID: {credential.id}, Status: {credential.status}")
import { GoogleGenAI } from "@google/genai";
const client = new GoogleGenAI({});
const credential = await client.credentials.create({
id: "github-production",
type: "bearer_token",
token: "«redacted:ghp_…»",
});
console.log(`Credential ID: ${credential.id}, Status: ${credential.status}`);
package main
import (
"context"
"fmt"
"log"
"google.golang.org/genai"
"google.golang.org/genai/interactions/models/credentials"
"google.golang.org/genai/interactions/models/operations"
)
func main() {
ctx := context.Background()
client, err := genai.NewClient(ctx, nil)
if err != nil {
log.Fatal(err)
}
res, err := client.Credentials.Create(ctx, operations.CreateCredentialRequest{
Body: credentials.NewCredentialCreateParams(credentials.HTTPBearerConfig{
ID: "github-production",
Token: "«redacted:ghp_…»",
}),
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Credential ID: %s, Status: %v\n", res.Credential.ID, res.Credential.GetStatus())
}
curl -X POST "https://generativelanguage.googleapis.com/v1beta/credentials" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"id": "github-production",
"type": "bearer_token",
"token": "«redacted:ghp_…»"
}'
그런 다음 인증할 도메인에 자격 증명을 연결하세요.
interaction = client.interactions.create(
agent="antigravity-preview-09-2026",
input="Triage the open issues in my-org/my-repo.",
environment={
"type": "remote",
"network": {
"allowlist": [
{"domain": "api.github.com", "credential": "github-production"},
{"domain": "*"},
]
},
},
)
const interaction = await client.interactions.create({
agent: "antigravity-preview-09-2026",
input: "Triage the open issues in my-org/my-repo.",
environment: {
type: "remote",
network: {
allowlist: [
{ domain: "api.github.com", credential: "github-production" },
{ domain: "*" },
],
},
},
});
package main
import (
"context"
"fmt"
"log"
"google.golang.org/genai"
"google.golang.org/genai/interactions/models/interactions"
"google.golang.org/genai/interactions/models/operations"
)
func main() {
ctx := context.Background()
client, err := genai.NewClient(ctx, nil)
if err != nil {
log.Fatal(err)
}
res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
Body: operations.NewCreateInteractionRequestBody(interactions.CreateAgentInteraction{
Agent: interactions.AgentOption("antigravity-preview-09-2026"),
Input: interactions.NewInteractionsInput("Triage the open issues in my-org/my-repo."),
Environment: genai.Ptr(interactions.NewCreateAgentInteractionEnvironment(interactions.Environment{
Network: genai.Ptr(interactions.NewNetwork(interactions.NewEnvironmentNetworkEgressAllowlist(interactions.Allowlist{
Allowlist: []interactions.AllowlistEntry{
{Domain: "api.github.com", Credential: genai.Ptr("github-production")},
{Domain: "*"},
},
}))),
})),
}),
})
if err != nil {
log.Fatal(err)
}
fmt.Println(res.Interaction.GetOutputText())
}
curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"agent": "antigravity-preview-09-2026",
"input": "Triage the open issues in my-org/my-repo.",
"environment": {
"type": "remote",
"network": {
"allowlist": [
{ "domain": "api.github.com", "credential": "github-production" },
{ "domain": "*" }
]
}
}
}'
이제 에이전트는 api.github.com에 인증된 요청을 하고, 토큰은 샌드박스 안에 절대 존재하지 않아요.
자격 증명 유형
모든 자격 증명은 type을 가지며, 이 필드가 어떤 필드를 받는지와 프록시가 어떻게 적용하는지는 결정해요.
| 유형 | 사용 사례 | 동작 |
|---|---|---|
bearer_token |
개인 액세스 토큰, 봇 토큰, 정적 API 키 | 프록시가 토큰을 요청 헤더로 주입. 새로고침 로직 없음. |
oauth2 |
OAuth 앱 및 사용자 위임 흐름 | 프록시가 refresh token을 access token으로 교환하고 만료 시 새로고침. |
environment_variable |
프로세스 환경에서 비밀값을 읽는 클라이언트 SDK | 에이전트 환경에 플레이스홀더가 주어짐. 프록시가 아웃바운드 요청에서 실제 비밀값으로 치환. |
네트워크 allowlist에서 자격 증명 사용하기
allowlist 규칙에 credential을 추가하면 프록시가 해당 도메인으로 가는 모든 아웃바운드 요청을 인증해요. 이것이 에이전트에게 비공개 API, 비공개 저장소, 비공개 버킷에 대한 접근을 주는 권장 방법이에요.
같은 allowlist 안에서 인증된 규칙과 인증되지 않은 규칙을 섞을 수 있어요.
interaction = client.interactions.create(
agent="antigravity-preview-09-2026",
input="Sync the open Jira issues into the tracking sheet in my repo.",
environment={
"type": "remote",
"sources": [
{
"type": "repository",
"source": "https://github.com/your-org/backend",
"target": "/backend-app",
}
],
"network": {
"allowlist": [
{"domain": "github.com", "credential": "github-production"},
{"domain": "api.atlassian.com", "credential": "jira-oauth"},
{"domain": "*.googleapis.com"},
]
},
},
)
const interaction = await client.interactions.create({
agent: "antigravity-preview-09-2026",
input: "Sync the open Jira issues into the tracking sheet in my repo.",
environment: {
type: "remote",
sources: [
{
type: "repository",
source: "https://github.com/your-org/backend",
target: "/backend-app",
},
],
network: {
allowlist: [
{ domain: "github.com", credential: "github-production" },
{ domain: "api.atlassian.com", credential: "jira-oauth" },
{ domain: "*.googleapis.com" },
],
},
},
});
package main
import (
"context"
"fmt"
"log"
"google.golang.org/genai"
"google.golang.org/genai/interactions/models/interactions"
"google.golang.org/genai/interactions/models/operations"
)
func main() {
ctx := context.Background()
client, err := genai.NewClient(ctx, nil)
if err != nil {
log.Fatal(err)
}
res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
Body: operations.NewCreateInteractionRequestBody(interactions.CreateAgentInteraction{
Agent: interactions.AgentOption("antigravity-preview-09-2026"),
Input: interactions.NewInteractionsInput("Sync the open Jira issues into the tracking sheet in my repo."),
Environment: genai.Ptr(interactions.NewCreateAgentInteractionEnvironment(interactions.Environment{
Sources: []interactions.Source{
{
Type: interactions.SourceTypeRepository.ToPointer(),
Source: genai.Ptr("https://github.com/your-org/backend"),
Target: genai.Ptr("/backend-app"),
},
},
Network: genai.Ptr(interactions.NewNetwork(interactions.NewEnvironmentNetworkEgressAllowlist(interactions.Allowlist{
Allowlist: []interactions.AllowlistEntry{
{Domain: "github.com", Credential: genai.Ptr("github-production")},
{Domain: "api.atlassian.com", Credential: genai.Ptr("jira-oauth")},
{Domain: "*.googleapis.com"},
},
}))),
})),
}),
})
if err != nil {
log.Fatal(err)
}
fmt.Println(res.Interaction.GetOutputText())
}
curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"agent": "antigravity-preview-09-2026",
"input": "Sync the open Jira issues into the tracking sheet in my repo.",
"environment": {
"type": "remote",
"sources": [
{
"type": "repository",
"source": "https://github.com/your-org/backend",
"target": "/backend-app"
}
],
"network": {
"allowlist": [
{ "domain": "github.com", "credential": "github-production" },
{ "domain": "api.atlassian.com", "credential": "jira-oauth" },
{ "domain": "*.googleapis.com" }
]
}
}
}'
프록시가 요청마다 자격 증명을 해석하므로 oauth2 자격 증명은 access token을 투명하게 새로고침해요. 장시간 실행되는 상호작용이 access token 만료로 끊기지 않아요.
credential과 transform 조합하기
allowlist 규칙은 규칙에 직접 헤더를 설정하는 인라인 transform 객체도 받아들여요. 두 메커니즘 모두 egress 프록시가 전선(wire)에서 적용하므로, 두 경우 모두 헤더 값이 샌드박스 안에 존재하지 않아요. 두 필드가 같은 규칙에 함께 나올 수 있어요.
| 규칙 구성 | 동작 |
|---|---|
credential만 |
프록시가 자격 증명을 해석해 해당 도메인으로 가는 모든 요청에 그 헤더를 주입. |
transform만 |
정적 헤더 주입. 작성한 헤더가 그대로 전송. |
| 둘 다 | 자격 증명이 먼저 적용되고 transform이 그 위에 병합. 같은 키를 둘 다 설정하면 명시적 transform 헤더가 이김. |
| 둘 다 없음 | 도메인은 허용되고 헤더는 주입되지 않음. |
비밀값을 한 번 저장해 프로젝트의 모든 환경·에이전트·트리거에서 참조하고 싶을 때, 그리고 access token 새로고침과 로테이션을 알아서 처리하고 싶을 때 자격 증명을 쓰는 게 좋아요. 값이 단일 호출에 속할 때, 예를 들어 상호작용을 만들기 직전에 직접 생성한 토큰일 때는 인라인 transform이 어울려요.
둘을 조합하는 것도 흔해요. 자격 증명은 인증 헤더를 담고 transform은 같은 요청에서 업스트림 서비스가 기대하는 다른 것을 더해요.
{
"domain": "api.atlassian.com",
"credential": "jira-oauth",
"transform": {
"X-Atlassian-Workspace": "my-workspace-id"
}
}
비밀값을 인라인 transform에서 자격 증명으로 옮기려면 POST /credentials로 저장하고, transform의 인증 헤더를 "credential": "<id>"로 바꾼 뒤 transform 객체의 나머지는 그대로 두세요.
MCP 서버와 자격 증명 사용하기
원격 MCP 서버도 같은 credential 필드를 받아요. mcp_server 도구에 설정하면 프록시가 그 서버로 가는 모든 요청에 인증 헤더를 주입해요.
interaction = client.interactions.create(
agent="antigravity-preview-09-2026",
input="Create a new issue in my-org/my-repo",
environment="remote",
tools=[{
"type": "mcp_server",
"name": "github",
"url": "https://api.githubcopilot.com/mcp",
"credential": "github-production",
}],
)
const interaction = await client.interactions.create({
agent: "antigravity-preview-09-2026",
input: "Create a new issue in my-org/my-repo",
environment: "remote",
tools: [{
type: "mcp_server",
name: "github",
url: "https://api.githubcopilot.com/mcp",
credential: "github-production",
}],
});
package main
import (
"context"
"fmt"
"log"
"google.golang.org/genai"
"google.golang.org/genai/interactions/models/interactions"
"google.golang.org/genai/interactions/models/operations"
)
func main() {
ctx := context.Background()
client, err := genai.NewClient(ctx, nil)
if err != nil {
log.Fatal(err)
}
res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
Body: operations.NewCreateInteractionRequestBody(interactions.CreateAgentInteraction{
Agent: interactions.AgentOption("antigravity-preview-09-2026"),
Input: interactions.NewInteractionsInput("Create a new issue in my-org/my-repo"),
Environment: genai.Ptr(interactions.NewCreateAgentInteractionEnvironment(interactions.Environment{
Network: genai.Ptr(interactions.NewNetwork(interactions.NewEnvironmentNetworkEgressAllowlist(interactions.Allowlist{
Allowlist: []interactions.AllowlistEntry{
{Domain: "api.githubcopilot.com", Credential: genai.Ptr("github-production")},
},
}))),
})),
Tools: []interactions.Tool{
interactions.NewTool(interactions.MCPServer{
Name: genai.Ptr("github"),
URL: genai.Ptr("https://api.githubcopilot.com/mcp"),
}),
},
}),
})
if err != nil {
log.Fatal(err)
}
fmt.Println(res.Interaction.GetOutputText())
}
curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"agent": "antigravity-preview-09-2026",
"input": "Create a new issue in my-org/my-repo",
"environment": "remote",
"tools": [
{
"type": "mcp_server",
"name": "github",
"url": "https://api.githubcopilot.com/mcp",
"credential": "github-production"
}
]
}'
credential과 headers는 allowlist와 같은 우선순위 규칙을 따르고, 자격 증명이 먼저 적용되며 headers가 그 위에 병합되므로 같은 키를 둘 다 설정하면 명시적 헤더가 이겨요.
{
"type": "mcp_server",
"name": "jira",
"url": "https://jira.atlassian.com/mcp",
"credential": "jira-oauth",
"headers": {
"X-Atlassian-Workspace": "my-workspace-id"
}
}
비밀값을 인라인 headers에서 자격 증명으로 옮기려면 POST /credentials로 저장하고 headers의 인증 항목을 credential로 바꾸세요. 다른 헤더는 그 자리에 두세요.
환경 변수로 자격 증명 사용하기
일부 클라이언트 라이브러리는 요청 헤더로 받기보다 프로세스 환경에서 비밀값을 읽어요. 소켓 모드·롱폴링 클라이언트가 대표적인 경우예요.
environment.env 아래의 변수 이름에 environment_variable 자격 증명을 바인딩하세요.
interaction = client.interactions.create(
agent="antigravity-preview-09-2026",
input="Run the sync script and check notifications.",
environment={
"type": "remote",
"env": {
"NODE_ENV": "production",
"SLACK_BOT_TOKEN": {"credential": "slack-bot-token"},
},
},
)
const interaction = await client.interactions.create({
agent: "antigravity-preview-09-2026",
input: "Run the sync script and check notifications.",
environment: {
type: "remote",
env: {
NODE_ENV: "production",
SLACK_BOT_TOKEN: { credential: "slack-bot-token" },
},
},
});
package main
import (
"context"
"fmt"
"log"
"google.golang.org/genai"
"google.golang.org/genai/interactions/models/interactions"
"google.golang.org/genai/interactions/models/operations"
)
func main() {
ctx := context.Background()
client, err := genai.NewClient(ctx, nil)
if err != nil {
log.Fatal(err)
}
res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
Body: operations.NewCreateInteractionRequestBody(interactions.CreateAgentInteraction{
Agent: interactions.AgentOption("antigravity-preview-09-2026"),
Input: interactions.NewInteractionsInput("Run the sync script and check notifications."),
Environment: genai.Ptr(interactions.NewCreateAgentInteractionEnvironment(interactions.Environment{
Env: genai.Ptr(interactions.NewEnv(map[string]interactions.EnvVar{
"NODE_ENV": {Value: genai.Ptr("production")},
"SLACK_BOT_TOKEN": {Credential: genai.Ptr("slack-bot-token")},
})),
})),
}),
})
if err != nil {
log.Fatal(err)
}
fmt.Println(res.Interaction.GetOutputText())
}
curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"agent": "antigravity-preview-09-2026",
"input": "Run the sync script and check notifications.",
"environment": {
"type": "remote",
"env": {
"NODE_ENV": "production",
"SLACK_BOT_TOKEN": { "credential": "slack-bot-token" }
}
}
}'
env는 리터럴 문자열과 자격 증명 참조를 나란히 받아들여요. 리터럴 문자열은 일반 평문 변수로 컨테이너에 주입돼요.
자격 증명 참조는 그렇지 않아요. 변수는 __GEMINI_CRED_<credential-id>__ 플레이스홀더를 받고, 프록시는 자격 증명의 trusted_domains에 있는 도메인으로 가는 아웃바운드 요청에서만 실제 비밀값을 교체해요. 다른 도메인으로 가는 요청은 거부되므로 비밀값이 경계를 벗어나지 않고 플레이스홀더도 그 자리에 전송되지 않아요.
주의: 리터럴 값은 평문으로 주입되며 샌드박스 안에서 실행되는 모든 것(에이전트 자신 포함)이 읽을 수 있어요. NODE_ENV 같은 설정에 쓰세요. 비밀값에는 쓰지 마세요. 비밀값은 플레이스홀더와 전선 치환을 받는 유일한 형태인 자격 증명에 속해요.
모든 environment_variable 자격 증명에 trusted_domains를 설정하세요. 이것이 비밀값을 어디에 쓸 수 있는지 범위를 정하는 제어 장치예요.
자격 증명 만들기
모든 생성 요청은 type과 그 유형이 요구하는 필드를 필요로 해요.
REST를 직접 호출할 때 모든 필드 이름은 snake_case를 사용해요. camelCase 필드를 보내면 400이 반환됩니다.
Bearer token
bearer token 자격 증명은 token만 필요해요.
credential = client.credentials.create(
id="github-production",
type="bearer_token",
token="«redacted:ghp_…»",
)
const credential = await client.credentials.create({
id: "github-production",
type: "bearer_token",
token: "«redacted:ghp_…»",
});
package main
import (
"context"
"fmt"
"log"
"google.golang.org/genai"
"google.golang.org/genai/interactions/models/credentials"
"google.golang.org/genai/interactions/models/operations"
)
func main() {
ctx := context.Background()
client, err := genai.NewClient(ctx, nil)
if err != nil {
log.Fatal(err)
}
res, err := client.Credentials.Create(ctx, operations.CreateCredentialRequest{
Body: credentials.NewCredentialCreateParams(credentials.HTTPBearerConfig{
ID: "github-production",
Token: "«redacted:ghp_…»",
}),
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Created credential: %s\n", res.Credential.ID)
}
curl -X POST "https://generativelanguage.googleapis.com/v1beta/credentials" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"id": "github-production",
"type": "bearer_token",
"token": "«redacted:ghp_…»"
}'
응답은 토큰이 아닌 메타데이터만 반환해요.
{
"id": "github-production",
"type": "bearer_token",
"status": "active",
"create_time": "2026-07-15T10:00:00.000000000Z",
"update_time": "2026-07-15T10:00:00.000000000Z"
}
기본적으로 프록시는 Authorization: Bearer <token>을 보내요. 다른 것을 기대하는 서비스를 대상으로 header_name과 prefix를 재정의하세요.
credential = client.credentials.create(
id="my-api-key",
type="bearer_token",
token="key_xxxxxxxxxxxx",
header_name="x-goog-api-key",
prefix="",
)
const credential = await client.credentials.create({
id: "my-api-key",
type: "bearer_token",
token: "key_xxxxxxxxxxxx",
header_name: "x-goog-api-key",
prefix: "",
});
package main
import (
"context"
"fmt"
"log"
"google.golang.org/genai"
"google.golang.org/genai/interactions/models/credentials"
"google.golang.org/genai/interactions/models/operations"
)
func main() {
ctx := context.Background()
client, err := genai.NewClient(ctx, nil)
if err != nil {
log.Fatal(err)
}
res, err := client.Credentials.Create(ctx, operations.CreateCredentialRequest{
Body: credentials.NewCredentialCreateParams(credentials.HTTPBearerConfig{
ID: "my-api-key",
Token: "key_xxxxxxxxxxxx",
HeaderName: genai.Ptr("x-goog-api-key"),
Prefix: genai.Ptr(""),
}),
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Created credential: %s\n", res.Credential.ID)
}
curl -X POST "https://generativelanguage.googleapis.com/v1beta/credentials" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"id": "my-api-key",
"type": "bearer_token",
"token": "key_xxxxxxxxxxxx",
"header_name": "x-goog-api-key",
"prefix": ""
}'
이 구성은 x-goog-api-key: key_xx...xx 헤더를 만들어요.
아래 표는 header_name과 prefix가 어떻게 결합하는지 보여줘요.
| 구성 | 주입된 헤더 |
|---|---|
{"token": "ghp_xxx"} |
Authorization: Bearer ghp_xxx |
{"token": "sk_live_xxx"} |
Authorization: Bearer sk_live_xxx |
{"token": "key_xxx", "header_name": "x-goog-api-key", "prefix": ""} |
x-goog-api-key: key_xxx |
{"token": "mytoken", "header_name": "X-API-Token", "prefix": ""} |
X-API-Token: mytoken |
OAuth2
OAuth2 자격 증명은 client_id, client_secret, refresh_token, token_url을 필요로 해요. scopes 필드는 선택사항이에요.
credential = client.credentials.create(
id="jira-oauth",
type="oauth2",
client_id="my-client-id",
client_secret="my-client-secret",
token_url="https://auth.atlassian.com/oauth/token",
refresh_token="rt_xxxxxxxxxxxxxxxxxxxx",
scopes=["read:jira-work", "write:jira-work"],
)
const credential = await client.credentials.create({
id: "jira-oauth",
type: "oauth2",
client_id: "my-client-id",
client_secret: "my-client-secret",
token_url: "https://auth.atlassian.com/oauth/token",
refresh_token: "rt_xxxxxxxxxxxxxxxxxxxx",
scopes: ["read:jira-work", "write:jira-work"],
});
package main
import (
"context"
"fmt"
"log"
"google.golang.org/genai"
"google.golang.org/genai/interactions/models/credentials"
"google.golang.org/genai/interactions/models/operations"
)
func main() {
ctx := context.Background()
client, err := genai.NewClient(ctx, nil)
if err != nil {
log.Fatal(err)
}
res, err := client.Credentials.Create(ctx, operations.CreateCredentialRequest{
Body: credentials.NewCredentialCreateParams(credentials.OAuth2Config{
ID: "jira-oauth",
ClientID: "my-client-id",
ClientSecret: "my-client-secret",
TokenURL: "https://auth.atlassian.com/oauth/token",
RefreshToken: "rt_xxxxxxxxxxxxxxxxxxxx",
Scopes: []string{"read:jira-work", "write:jira-work"},
}),
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Created OAuth2 credential: %s\n", res.Credential.ID)
}
curl -X POST "https://generativelanguage.googleapis.com/v1beta/credentials" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"id": "jira-oauth",
"type": "oauth2",
"client_id": "my-client-id",
"client_secret": "my-client-secret",
"token_url": "https://auth.atlassian.com/oauth/token",
"refresh_token": "rt_xxxxxxxxxxxxxxxxxxxx",
"scopes": ["read:jira-work", "write:jira-work"]
}'
OAuth2 자격 증명을 만들면 구성이 맞는지 확인하기 위해 token_url에 대해 실제 토큰 교환을 수행해요. 자격 증명은 제공자가 access_token을 담은 성공적인 토큰 응답을 반환할 때만 저장돼요. JSON과 form-urlencoded 응답 모두 허용되어요.
즉 생성 시점에 유효하고 만료되지 않은 refresh token이 필요하다는 뜻이에요. 제공자가 교환을 거부하면 오류가 반환돼요.
{
"error": {
"message": "OAuth token validation failed with HTTP 403: {\"error\":\"unauthorized_client\",\"error_description\":\"refresh_token is invalid\"}",
"code": "invalid_request"
}
}
일단 저장되면 프록시가 access token을 만료 시 새로고침해요. 제공자가 refresh token을 로테이션하고 새로고침 중에 새 것을 반환하면 새 토큰이 저장된 것을 자동으로 대체해요.
환경 변수
environment_variable 자격 증명은 value와 injection_location을 필요로 해요.
credential = client.credentials.create(
id="slack-bot-token",
type="environment_variable",
value="«redacted:xox…»",
trusted_domains=["*.slack.com", "slack.com"],
injection_location="header",
)
const credential = await client.credentials.create({
id: "slack-bot-token",
type: "environment_variable",
value: "«redacted:xox…»",
trusted_domains: ["*.slack.com", "slack.com"],
injection_location: "header",
});
package main
import (
"context"
"fmt"
"log"
"google.golang.org/genai"
"google.golang.org/genai/interactions/models/credentials"
"google.golang.org/genai/interactions/models/operations"
)
func main() {
ctx := context.Background()
client, err := genai.NewClient(ctx, nil)
if err != nil {
log.Fatal(err)
}
res, err := client.Credentials.Create(ctx, operations.CreateCredentialRequest{
Body: credentials.NewCredentialCreateParams(credentials.EnvironmentVariableConfig{
ID: "slack-bot-token",
Value: "«redacted:xox…»",
TrustedDomains: []string{"*.slack.com", "slack.com"},
InjectionLocation: credentials.NewEnvironmentVariableConfigInjectionLocation(credentials.InjectionLocationEnumHeader),
}),
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Created environment variable credential: %s\n", res.Credential.ID)
}
curl -X POST "https://generativelanguage.googleapis.com/v1beta/credentials" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"id": "slack-bot-token",
"type": "environment_variable",
"value": "«redacted:xox…»",
"trusted_domains": ["*.slack.com", "slack.com"],
"injection_location": "header"
}'
injection_location 필드는 아웃바운드 요청의 어디에서 비밀값을 치환할지 프록시에 알려줘요. header, query, body 중 하나를 받고, 서비스가 둘 이상 필요할 때는 단일 문자열 또는 배열로 받아요.
"injection_location": ["header", "query"]
치환은 나열한 위치에서만 일어나요. 다른 위치에 플레이스홀더를 담은 요청은 전송되지 않고 거부돼요.
자격 증명을 변수 이름에 바인딩하려면 환경 변수로 자격 증명 사용하기를 참고하세요.
생성된 ID
id 필드는 선택사항이에요. 생략하면 서비스가 UUID를 생성해요.
{
"id": "9e545973-4330-49bb-9a44-930cea9fbe3c",
"type": "bearer_token",
"status": "active",
"create_time": "2026-07-15T10:00:00.000000000Z",
"update_time": "2026-07-15T10:00:00.000000000Z"
}
상호작용 전반에 걸쳐 사용할 안정적이고 읽기 쉬운 참조가 필요할 때는 자신의 ID를 제공하세요. ID는 리소스 경로에 나타나므로 소문자 영숫자에 하이픈 또는 밑줄을 쓰는 것을 권장해요.
자격 증명 나열하기
프로젝트에 속한 자격 증명을 나열해요. 페이징 파라미터로 응답 배치 크기를 제어할 수 있어요.
response = client.credentials.list(page_size=10)
for credential in response.credentials:
print(f"Credential ID: {credential.id}, Type: {credential.type}")
const response = await client.credentials.list({ page_size: 10 });
for (const credential of response.credentials) {
console.log(`Credential ID: ${credential.id}, Type: ${credential.type}`);
}
package main
import (
"context"
"fmt"
"log"
"google.golang.org/genai"
"google.golang.org/genai/interactions/models/operations"
)
func main() {
ctx := context.Background()
client, err := genai.NewClient(ctx, nil)
if err != nil {
log.Fatal(err)
}
res, err := client.Credentials.List(ctx, operations.ListCredentialsRequest{
PageSize: genai.Ptr(10),
})
if err != nil {
log.Fatal(err)
}
for _, cred := range res.CredentialListResponse.Credentials {
fmt.Printf("Credential ID: %s, Type: %v\n", cred.ID, cred.GetType())
}
}
curl -X GET "https://generativelanguage.googleapis.com/v1beta/credentials?page_size=10" \
-H "x-goog-api-key: $GEMINI_API_KEY"
응답은 메타데이터만 포함해요.
{
"credentials": [
{
"id": "github-production",
"type": "bearer_token",
"status": "active",
"create_time": "2026-07-15T10:00:00.000000000Z",
"update_time": "2026-07-15T10:00:00.000000000Z"
},
{
"id": "jira-oauth",
"type": "oauth2",
"status": "active",
"create_time": "2026-07-15T10:05:00.000000000Z",
"update_time": "2026-07-15T10:05:00.000000000Z"
}
],
"next_page_token": "Cj...5aE="
}
next_page_token을 page_token으로 다시 전달해 다음 페이지를 가져와요. 더 이상 결과가 없으면 이 필드는 생략돼요.
| 파라미터 | 유형 | 설명 |
|---|---|---|
page_size |
integer | 페이지당 최대 자격 증명 수. |
page_token |
string | 이전 응답의 next_page_token에서 온 토큰. |
자격 증명 가져오기
ID로 특정 자격 증명의 메타데이터를 검색해요.
credential = client.credentials.get(id="github-production")
print(f"Credential ID: {credential.id}, Status: {credential.status}")
const credential = await client.credentials.get("github-production");
console.log(`Credential ID: ${credential.id}, Status: ${credential.status}`);
package main
import (
"context"
"fmt"
"log"
"google.golang.org/genai"
"google.golang.org/genai/interactions/models/operations"
)
func main() {
ctx := context.Background()
client, err := genai.NewClient(ctx, nil)
if err != nil {
log.Fatal(err)
}
res, err := client.Credentials.Get(ctx, operations.GetCredentialRequest{
ID: "github-production",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Credential ID: %s, Status: %v\n", res.Credential.ID, res.Credential.GetStatus())
}
curl -X GET "https://generativelanguage.googleapis.com/v1beta/credentials/github-production" \
-H "x-goog-api-key: $GEMINI_API_KEY"
응답은 다음과 비슷해요.
{
"id": "github-production",
"type": "bearer_token",
"status": "active",
"create_time": "2026-07-15T10:00:00.000000000Z",
"update_time": "2026-08-01T14:30:00.000000000Z"
}
존재하지 않는 자격 증명을 요청하면 404가 반환돼요.
{
"error": {
"message": "Result not found.; GetCredential call failed",
"code": "not_found"
}
}
자격 증명 회전 (Rotate)
그것을 참조하는 allowlist 규칙, 도구 정의, 환경 변수를 건드리지 않고 비밀값을 교체해요. 회전은 다음 프록시 해석 때 적용돼요.
요청은 type과 변경하려는 필드를 반드시 포함해야 해요. 생략한 필드는 현재 값을 유지해요.
bearer token 회전:
credential = client.credentials.update(
id="github-production",
type="bearer_token",
token="ghp_new_xxxxxxxxxxxxxxxxxxxx",
)
const credential = await client.credentials.update("github-production", {
type: "bearer_token",
token: "ghp_new_xxxxxxxxxxxxxxxxxxxx",
});
package main
import (
"context"
"fmt"
"log"
"google.golang.org/genai"
"google.golang.org/genai/interactions/models/credentials"
"google.golang.org/genai/interactions/models/operations"
)
func main() {
ctx := context.Background()
client, err := genai.NewClient(ctx, nil)
if err != nil {
log.Fatal(err)
}
res, err := client.Credentials.Update(ctx, operations.UpdateCredentialRequest{
ID: "github-production",
Body: credentials.NewCredentialUpdate(credentials.HTTPBearerUpdateConfig{
Token: genai.Ptr("ghp_new_xxxxxxxxxxxxxxxxxxxx"),
}),
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Updated credential %s at %v\n", res.Credential.ID, res.Credential.GetUpdateTime())
}
curl -X PATCH "https://generativelanguage.googleapis.com/v1beta/credentials/github-production" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"type": "bearer_token",
"token": "ghp_new_xxxxxxxxxxxxxxxxxxxx"
}'
OAuth2 refresh token 회전:
credential = client.credentials.update(
id="jira-oauth",
type="oauth2",
refresh_token="rt_new_xxxxxxxxxxxxxxxxxxxx",
)
const credential = await client.credentials.update("jira-oauth", {
type: "oauth2",
refresh_token: "rt_new_xxxxxxxxxxxxxxxxxxxx",
});
package main
import (
"context"
"fmt"
"log"
"google.golang.org/genai"
"google.golang.org/genai/interactions/models/credentials"
"google.golang.org/genai/interactions/models/operations"
)
func main() {
ctx := context.Background()
client, err := genai.NewClient(ctx, nil)
if err != nil {
log.Fatal(err)
}
res, err := client.Credentials.Update(ctx, operations.UpdateCredentialRequest{
ID: "jira-oauth",
Body: credentials.NewCredentialUpdate(credentials.OAuth2UpdateConfig{
RefreshToken: genai.Ptr("rt_new_xxxxxxxxxxxxxxxxxxxx"),
}),
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Updated credential %s at %v\n", res.Credential.ID, res.Credential.GetUpdateTime())
}
curl -X PATCH "https://generativelanguage.googleapis.com/v1beta/credentials/jira-oauth" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
"type": "oauth2",
"refresh_token": "rt_new_xxxxxxxxxxxxxxxxxxxx"
}'
응답은 새로운 update_time을 반영해요.
{
"id": "jira-oauth",
"type": "oauth2",
"status": "active",
"create_time": "2026-07-15T10:05:00.000000000Z",
"update_time": "2026-08-01T14:30:00.000000000Z"
}
자격 증명의 type은 생성 시점에 고정돼요. 바꾸려면 자격 증명을 삭제하고 새로 만들어야 해요.
자격 증명 삭제하기
더 이상 필요 없을 때 자격 증명과 저장된 비밀값을 삭제해요.
client.credentials.delete(id="github-production")
await client.credentials.delete("github-production");
package main
import (
"context"
"log"
"google.golang.org/genai"
"google.golang.org/genai/interactions/models/operations"
)
func main() {
ctx := context.Background()
client, err := genai.NewClient(ctx, nil)
if err != nil {
log.Fatal(err)
}
_, err = client.Credentials.Delete(ctx, operations.DeleteCredentialRequest{
ID: "github-production",
})
if err != nil {
log.Fatal(err)
}
}
curl -X DELETE "https://generativelanguage.googleapis.com/v1beta/credentials/github-production" \
-H "x-goog-api-key: $GEMINI_API_KEY"
성공적인 삭제는 빈 객체를 반환해요.
{}
그 ID를 여전히 참조하는 allowlist 규칙, 도구, 환경 변수는 해석에 실패하므로 먼저 그런 것들을 업데이트하세요.
필드 참조
모든 자격 증명에 공통인 필드:
| 필드 | 유형 | 필수 | 설명 |
|---|---|---|---|
id |
string | 아니요 | 고유 식별자. 생략하면 UUID로 생성. |
type |
string | 예 | bearer_token, oauth2, environment_variable 중 하나. |
status |
string | 읽기 전용 | 자격 증명의 현재 상태. |
create_time |
string | 읽기 전용 | RFC 3339 생성 타임스탬프. |
update_time |
string | 읽기 전용 | 마지막 업데이트의 RFC 3339 타임스탬프. |
bearer_token 필드:
| 필드 | 유형 | 필수 | 설명 |
|---|---|---|---|
token |
string | 예 | 쓰기 전용. 토큰 값. |
header_name |
string | 아니요 | 주입할 헤더. 기본값 Authorization. |
prefix |
string | 아니요 | 값 접두사. 기본값 Bearer. 없으려면 ""로 설정. |
oauth2 필드:
| 필드 | 유형 | 필수 | 설명 |
|---|---|---|---|
client_id |
string | 예 | OAuth2 클라이언트 ID. |
client_secret |
string | 예 | 쓰기 전용. OAuth2 클라이언트 시크릿. |
refresh_token |
string | 예 | 쓰기 전용. access token을 얻는 데 사용하는 refresh token. |
token_url |
string | 예 | 제공자 토큰 엔드포인트. |
scopes |
array | 아니요 | 요청할 OAuth 스코프. |
environment_variable 필드:
| 필드 | 유형 | 필수 | 설명 |
|---|---|---|---|
value |
string | 예 | 쓰기 전용. 비밀값. |
injection_location |
string 또는 array | 예 | 비밀값을 치환할 위치. header, query, body 중 하나 이상. |
trusted_domains |
array | 아니요 | 치환이 허용되는 도메인 패턴. |
오류
오류는 message와 code를 담은 JSON 객체를 반환해요.
{
"error": {
"message": "Credential 'github-production' already exists.; CreateCredential call failed",
"code": "aborted"
}
}
| HTTP 상태 | code |
원인 |
|---|---|---|
| 400 | invalid_request |
필수 필드 누락, 알 수 없는 필드, 미지원 type, 또는 실패한 OAuth2 검증. |
| 404 | not_found |
그 ID의 자격 증명이 없음. |
| 409 | aborted |
그 ID의 자격 증명이 이미 존재. |
알 수 없는 필드는 무시되지 않고 거부되며, 오류가 그 필드를 명명해요.
{
"error": {
"message": "Unknown parameter 'headerName'. Did you mean 'header_name'?",
"code": "invalid_request"
}
}
다음 단계
- Environments: 에이전트가 코드를 실행하고 파일을 유지하는 방법.
- Agents Overview: 관리형 에이전트의 핵심 개념.
- Building Custom Agents:
AGENTS.md와SKILL.md로 나만의 에이전트 정의하기.
더 알아보기 (Learn more)
자격 증명은 비밀값을 샌드박스 밖에 두면서 에이전트가 인증된 요청을 하게 하는 핵심 보안 장치예요. 여기서는 API 호출 방식(네트워크 allowlist, 환경 변수, MCP 서버)을 다양하게 다뤘어요. 에이전트가 코드와 파일을 어떻게 다루는지는 agent-environment 문서, 커스텀 에이전트 정의는 custom-agents 문서를 이어서 보면 좋아요.