Momento API Key と トークンの違いについて
先日もめんと会でドキュメントの輪読をした際に、API Key と トークンの違いについて議論がありました。少しわかりずらいので改めて解説したいと思います。
ドキュメントヒストリーなどを見ているとどうも、API Key と トークンの取り扱いや言葉の定義に変遷があり、Auth Tokenなどという表現もありますが、この記事では API Key と トークンで言葉を統一します。
API Key
マネージメントコンソールから取得可能なトークンです。マネージメントコンソールでも「トークン」と表現しているので勘違いしそうになりますが、こちらはAPI Key
です。
キャッシュ(名前空間)単位、オペレーション(読み書き)単位で設定が可能です。
基本この鍵は有効期間内はStaticであり、Rotationが推奨されています。主にバックエンドサーバからMomentoへの通信に用いられます。
リフレッシュトークンは、API Keyを更新する際に用いるものです。
トークン
フロントエンドプログラムから個別キャッシュキーを操作する際に利用するものです。
キャッシュ:名前空間
キー:キャッシュの一意識別子
アイテム:キャッシュのデータ
トークンはDisposableTokenScopes
でその権限範囲を設定します。
特定のキーに対する特定のオペレーションという限られた範囲で有効になります。
const oneKeyOneCacheToken = await authClient.generateDisposableToken(
DisposableTokenScopes.cacheKeyReadWrite('default-cache', 'mo'),
ExpiresIn.minutes(30)
);
の場合、default-cache
の名前空間に存在しているmo
というキーに対して、読み書きの権限が30分だけ付与される一時トークンを発行します。
特定ユーザーに対する一時的なトークンの発行をこまめに行うことで、トークンの危殆化が懸念される際に、システム全体のAPI Keyの入れ替えを防ぐことが出来ます。
以下のページに細かい定義があります。
Auth API
と記載されておりややこしいのですが、これはトークンです。
サンプルコード
以下でトークンのテストが可能です。
const { CacheGet, CacheSet, CacheClient, Configurations, CredentialProvider, AuthClient, DisposableTokenScopes, ExpiresIn, GenerateDisposableToken } = require('@gomomento/sdk');
async function main() {
let momento;
const authClient = new AuthClient({
credentialProvider: CredentialProvider.fromString({
apiKey: "<api key>",
}),
});
// Generate a disposable token with read-write access to a specific key in one cache
const oneKeyOneCacheToken = await authClient.generateDisposableToken(
DisposableTokenScopes.cacheKeyReadWrite('default-cache', 'mo'),
ExpiresIn.minutes(30)
);
if (oneKeyOneCacheToken instanceof GenerateDisposableToken.Success) {
console.log('Generated a disposable API key with access to the "mo" key in the "default-cache" cache!');
momento = await CacheClient.create({
configuration: Configurations.Laptop.v1(),
credentialProvider: CredentialProvider.fromString({
authToken: oneKeyOneCacheToken.authToken,
}),
defaultTtlSeconds: 60,
});
console.log(`API key starts with: ${oneKeyOneCacheToken.authToken.substring(0, 20)}`);
console.log(`Expires At: ${oneKeyOneCacheToken.expiresAt.epoch()}`);
} else if (oneKeyOneCacheToken instanceof GenerateDisposableToken.Error) {
throw new Error(
`An error occurred while attempting to call generateApiKey with disposable token scope: ${oneKeyOneCacheToken.errorCode()}: ${oneKeyOneCacheToken.toString()}`
);
}
console.log('Storing key=default-cache, value=FOO');
const setResponse = await momento?.set('default-cache', 'mo', 'FOO');
if (setResponse instanceof CacheSet.Success) {
console.log('Key stored successfully!');
} else {
console.log(`Error setting key: ${setResponse?.toString()}`);
}
const getResponse = await momento?.get('default-cache', 'mo');
if (getResponse instanceof CacheGet.Hit) {
console.log(`cache hit: ${getResponse.valueString()}`);
} else if (getResponse instanceof CacheGet.Miss) {
console.log('cache miss');
} else if (getResponse instanceof CacheGet.Error) {
console.log(`Error: ${getResponse.message()}`);
}
}
main()
.then(() => {
console.log('success!!');
})
.catch((e) => {
console.error(`Uncaught exception while running example: ${e.message}`);
throw e;
});
まず以下の部分でauthClient
にAPI Keyをセットします。
const authClient = new AuthClient({
credentialProvider: CredentialProvider.fromString({
apiKey: "<api key>",
}),
});
トークンはAPI Keyから派生されますが、Fine-Graded Access Tokenからの派生は行えまん。以下のエラーが出ます。
Error: An error occurred while attempting to call generateApiKey with disposable token scope: INVALID_ARGUMENT_ERROR: Invalid argument passed to Momento client: 3 INVALID_ARGUMENT: invalid parameter: auth_token, Only tokens with SuperUser permissions can generate disposable tokens
次に以下の部分でトークンを発行します。
const oneKeyOneCacheToken = await authClient.generateDisposableToken(
DisposableTokenScopes.cacheKeyReadWrite('default-cache', 'mo'),
ExpiresIn.minutes(30)
);
DisposableTokenScopes
でdefault-cache
という名前空間に存在しているmo
というキーに限定して値の読み書きを可能とします。(cacheKeyReadWrite
)
またこの一時トークンは30分有効です。派生されたトークンを用いて以下でCacheClientを生成しています。
momento = await CacheClient.create({
configuration: Configurations.Laptop.v1(),
credentialProvider: CredentialProvider.fromString({
authToken: oneKeyOneCacheToken.authToken,
}),
defaultTtlSeconds: 60,
});
このクライアントは、default-cache
という名前空間に存在しているmo
というキーに限定して値の読み書きを30分間可能とします。試しに以下の部分の20
を例えば300
などにしてみるとAPI Keyと異なる値がセットされていることがわかります。
console.log(`API key starts with: ${oneKeyOneCacheToken.authToken.substring(0, 20)}`);
API Key と トークンの実体
Base64 エンコードされたJWTトークンがその実態です。
Base64エンコードすると以下のようになります。
{
"endpoint":"cell-ap-northeast-1-1.prod.a.momentohq.com",
"api_key":"<key>"
}
さらにkey
の部分をJWTでコードすると以下が出力されます。
{
"typ": "JWT",
"alg": "HS256"
}
{
"sub": "momentbigfun@gmail.com",
"ver": 1,
"exp": 1697522409,
"p": "<token>",
"t": "disposable"
}
disposable
となっているのがトークンです。API Keyにはt
のフィールドは存在していません。
Discussion