APIを保護する際には、2つの異なる概念を理解することが重要です:
Goaは、サービスの認証と認可の要件を定義するためのDSL構成要素を提供します。
JWTは、JSON オブジェクトとして情報を安全に送信するためのコンパクトな方法を定義する オープン標準(RFC 7519)です。JWTは認証と 認可の両方によく使用されます:
var JWTAuth = JWTSecurity("jwt", func() {
Description("JWT ベースの認証と認可")
// スコープはJWTクレームに対してチェックできる権限を定義します
Scope("api:read", "読み取り専用アクセス")
Scope("api:write", "読み書きアクセス")
})
スコープは、クライアントが実行を許可されるアクションを表す名前付きの権限です。 JWTを使用する場合:
APIキーは、クライアントがリクエストに含める単純な文字列トークンです。一般的に 「APIキー認証」と呼ばれますが、より正確には認可メカニズムとして説明されます:
var APIKeyAuth = APIKeySecurity("api_key", func() {
Description("APIキーベースのリクエスト認可")
})
APIキーの一般的な用途:
Basic認証は、HTTPプロトコルに組み込まれたシンプルな認証スキームです:
var BasicAuth = BasicAuthSecurity("basic", func() {
Description("ユーザー名/パスワード認証")
// ここでのスコープは、認証成功後に付与できる権限を定義します
Scope("api:read", "読み取り専用アクセス")
})
OAuth2は、異なる種類のアプリケーション向けに複数のフローをサポートする包括的な認可 フレームワークです。以下を分離します:
var OAuth2Auth = OAuth2Security("oauth2", func() {
// OAuth2フローのエンドポイントを定義
AuthorizationCodeFlow(
"http://auth.example.com/authorize", // 認可をリクエストする場所
"http://auth.example.com/token", // コードをトークンと交換する場所
"http://auth.example.com/refresh", // 期限切れトークンを更新する場所
)
// 利用可能な権限を定義
Scope("api:read", "読み取り専用アクセス")
Scope("api:write", "読み書きアクセス")
})
セキュリティスキームは異なるレベルで適用できます:
個々のメソッドを1つまたは複数のスキームで保護します:
Method("secure_endpoint", func() {
Security(JWTAuth, func() {
Scope("api:read")
})
Payload(func() {
TokenField(1, "token", String)
Required("token")
})
HTTP(func() {
GET("/secure")
Response(StatusOK)
})
})
セキュリティを強化するために複数のセキュリティスキームを組み合わせます:
Method("doubly_secure", func() {
Security(JWTAuth, APIKeyAuth, func() {
Scope("api:write")
})
Payload(func() {
TokenField(1, "token", String)
APIKeyField(2, "api_key", "key", String)
Required("token", "key")
})
HTTP(func() {
POST("/secure")
Param("key:k") // クエリパラメータのAPIキー
Response(StatusOK)
})
})
セキュリティ認証情報がHTTP経由で送信される方法を設定します:
Method("secure_endpoint", func() {
Security(JWTAuth)
Payload(func() {
TokenField(1, "token", String)
Required("token")
})
HTTP(func() {
GET("/secure")
Header("token:Authorization") // AuthorizationヘッダーのJWT
Response(StatusOK)
Response("unauthorized", StatusUnauthorized)
})
})
gRPCトランスポートのセキュリティを設定します:
Method("secure_endpoint", func() {
Security(JWTAuth, APIKeyAuth)
Payload(func() {
TokenField(1, "token", String)
APIKeyField(2, "api_key", "key", String)
Required("token", "key")
})
GRPC(func() {
Metadata(func() {
Attribute("token:authorization") // メタデータのJWT
Attribute("api_key:x-api-key") // メタデータのAPIキー
})
Response(CodeOK)
Response("unauthorized", CodeUnauthenticated)
})
})
セキュリティ関連のエラーを一貫して定義します:
Service("secure_service", func() {
Error("unauthorized", String, "無効な認証情報")
Error("forbidden", String, "無効なスコープ")
HTTP(func() {
Response("unauthorized", StatusUnauthorized)
Response("forbidden", StatusForbidden)
})
GRPC(func() {
Response("unauthorized", CodeUnauthenticated)
Response("forbidden", CodePermissionDenied)
})
})
認証設計
認可設計
一般的なヒント
デザインでセキュリティスキームを定義すると、Goaはデザインに固有のAuther
インター
フェースを生成し、サービスはこれを実装する必要があります。このインターフェースは、
指定した各セキュリティスキームのメソッドを定義します:
// Autherはサービスのセキュリティ要件を定義します。
type Auther interface {
// BasicAuthはbasic認証の認可ロジックを実装します。
BasicAuth(context.Context, string, string, *security.BasicScheme) (context.Context, error)
}