この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
CognitoをオーソライザーにしたAPI Gatewayの認証情報をLambdaの中で取得する方法を調べました。言語はC#で.NET Coreのバージョンは3.1を使っています。
手順
- Cognitoのユーザプールとユーザーを作成してください。ユーザーとアプリクライアントを1件登録しておいてください。
- 下のソースコードでLambdaファンクションを作成してください。
- API Gatewayを使ってAPIを作成します。作成したLambdaファンクションを呼び出します。「プロキシ統合の使用」にチェックを入れてください。
- API Gatewayのオーソライザーは作成したCognitoのユーザープールを設定します。トークンのソースは今回は「Authorization」にしました。
- 認証情報を取得できているか確認します。私はテストアプリを作りました。
参照しているパッケージ
Lambdaのパッケージは以下を参照しています。
- Amazon.Lambda.APIGatewayEvents 2.4.0
- Amazon.Lambda.Core 1.2.0
- Amazon.Lambda.Serialization.SystemTextJson 2.1.0
- AWSSDK.APIGateway 3.5.2.15
- AWSSDK.Lambda 3.5.5.3
Lambdaのソースコード
以下、Lambdaのソースコードになります。内容はHTTPヘッダーにあるIDトークンを解析して、Cognitoに登録したメールアドレスを返しています。
using Amazon.Lambda.Core;
using System;
using System.Net;
using System.Text.Json;
using System.Text.Json.Serialization;
using Amazon.Lambda.APIGatewayEvents;
using System.Text;
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
namespace CognitoTest
{
public class Function
{
public APIGatewayProxyResponse FunctionHandler(Request request, ILambdaContext context)
{
var idToken = request.Headers.GetProperty("Authorization").GetString();
var section = idToken.Split('.')[1];
var padded = section.PadRight(section.Length + (section.Length * 3) % 4, '=');
var decoded = Encoding.UTF8.GetString(Convert.FromBase64String(padded));
string email = "";
using (JsonDocument document = JsonDocument.Parse(decoded))
{
JsonElement root = document.RootElement;
email = root.GetProperty("email").GetString();
}
return new APIGatewayProxyResponse
{
StatusCode = (int)HttpStatusCode.OK,
Body = email
};
}
}
public class Request
{
[JsonPropertyName("body")]
public string Body { get; set; }
[JsonPropertyName("httpMethod")]
public string HttpMethod { get; set; }
[JsonPropertyName("headers")]
public JsonElement Headers { get; set; }
[JsonPropertyName("requestContext")]
public JsonElement RequestContext { get; set; }
}
}
追記
後で気づいたのですが、メールアドレスやCognitoユーザーIDの取得であれば、requestContextというプロパティに入っていました。以下のようにしてメールアドレスを取得できます。HTTPヘッダーからIDトークンを取得するよりも、こちらの方が簡単でいいですね。
var authorizer = request.RequestContext.GetProperty("authorizer");
var claims = authorizer.GetProperty("claims");
var mail = claims.GetProperty("email").GetString();
確認する
私はUWPで簡単なテストアプリを作って確認しました。画面側はボタン1つだけの画面なので割愛します。やっていることはCognitoへのログインとAPIの呼び出しです。Cognitoにログインした際に取得したIDトークンをAPI呼び出し時にHTTPヘッダーに入れています。パッケージは以下をダウンロードしました。
- Amazon.Extensions.CognitoAuthentication.2.0.0
- AWSSDK.CognitoIdentity.3.5.1.9
- AWSSDK.CognitoIdentityProvider.3.5.1.7
using Amazon;
using Amazon.CognitoIdentityProvider;
using Amazon.Extensions.CognitoAuthentication;
using System;
using System.Diagnostics;
using System.Net.Http;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace CongnitoTest
{
public sealed partial class MainPage : Page
{
// お使いの環境の情報を設定してください。
private const string POOL_ID = "プールID";
private const string CLIENT_ID = "クライアントID";
private const string USERNAME = "ユーザー名";
private const string PASSWORD = "パスワード";
private const string URI = "URI";
public MainPage()
{
this.InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
// 認証
var provider = new AmazonCognitoIdentityProviderClient(null, RegionEndpoint.APNortheast1);
CognitoUserPool userPool = new CognitoUserPool(
POOL_ID,
CLIENT_ID,
provider
);
CognitoUser user = new CognitoUser(
USERNAME,
CLIENT_ID,
userPool,
provider
);
AuthFlowResponse response = await user.StartWithSrpAuthAsync(new InitiateSrpAuthRequest()
{
Password = PASSWORD
}).ConfigureAwait(false);
// API
var httpRequest = new HttpRequestMessage(HttpMethod.Post, new Uri(URI));
string idToken = response.AuthenticationResult.IdToken;
httpRequest.Headers.Add("Authorization", idToken);
using (var client = new HttpClient())
{
var res = await client.SendAsync(httpRequest);
if (res.StatusCode.Equals(System.Net.HttpStatusCode.OK))
{
Debug.WriteLine("success:" + await res.Content.ReadAsStringAsync());
}
else
{
Debug.WriteLine("failure");
}
}
}
}
}