あるSEのつぶやき・改

ITやシステム開発などの技術に関する話題を、取り上げたりしています。

FacebookのOAuth認証で取得したアクセストークンをC#で検証する方法

はじめに

以下の記事で、Angularな SPA で Facebook の OAuth 認証を行いました。

www.aruse.net

OAuth 認証時に取得したアクセストークンは、サーバーに送信して検証をしなければいけないはずなのですが、Facebook がその情報をきちんと公開していません。

検索を駆使してやっと見つけたのが以下の記事です。

medium.com

言語は C# ではないのですが、内容を変換しながらコーディングし、実際に動作したソースコードをご紹介します。

なお、環境は以下のようになります。

  • Windows 10
  • Visual Studio Community 2017
  • .NET Core 2.1
  • コンソールアプリケーション

Visual Studio の設定

Visual Studio で、.NET Core のコンソールアプリケーションテンプレートより、コンソールアプリケーションのプロジェクトを作成します。

また、JSON を扱うため、Json.NETをNugetよりインストールします。

サンプルソース

C# のコンソールアプリケーションで、Facebook の OAuth 認証で取得したアクセストークンを検証するソースコードは以下のようになります。

using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Collections.Generic;
using Newtonsoft;
using Newtonsoft.Json;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            Task task = ValidateAccessTokenAsync();
            task.Wait();

            Console.ReadKey();
        }

        static async Task ValidateAccessTokenAsync()
        {
            // アプリケーション設定情報
            string clientId = "アプリ ID";
            string clientSecret = "app secret";

            // ログイン画面で取得したアクセストークン
            string userToken = "アクセストークン";

            // 検証用に取得するアクセストークン
            string appToken;

            // 検証用に取得するユーザーID
            string userId;


            // アクセストークンを再度取得
            var parameters = new Dictionary<string, string>()
            {
                { "client_id", clientId },
                { "client_secret", clientSecret },
                { "grant_type", "client_credentials"}
            };

            HttpClient httpClient = new HttpClient();

            var response = 
                await httpClient.GetAsync($"https://graph.facebook.com/oauth/access_token?{await new FormUrlEncodedContent(parameters).ReadAsStringAsync()}");

            if ((int)response.StatusCode == 200)
            {
                AccessToken ret = JsonConvert.DeserializeObject<AccessToken>( await response.Content.ReadAsStringAsync());
                appToken = ret.access_token;

                Console.WriteLine("appToken:" + ret.access_token);
            }
            else
            {
                Console.WriteLine("Error:" + response.ReasonPhrase);
                Console.WriteLine("Access Token is invalid !!");
                return;
            }


            // アクセストークンの検証

            var parameters2 = new Dictionary<string, string>()
            {
                { "input_token", userToken },
                { "access_token", appToken }
            };

            var response2 =
                await httpClient.GetAsync($"https://graph.facebook.com/debug_token?{await new FormUrlEncodedContent(parameters2).ReadAsStringAsync()}");

            if ((int)response2.StatusCode == 200)
            {
                ValidateObject ret = JsonConvert.DeserializeObject<ValidateObject>(await response2.Content.ReadAsStringAsync());
                userId = ret.data.user_id;

                // ユーザー ID が取得できたときのみ Valid。
                if (string.IsNullOrEmpty(userId))
                {
                    Console.WriteLine("Access Token is invalid !!");
                    return;
                }

                Console.WriteLine("userId:" + userId);
            }
            else
            {
                Console.WriteLine("Error:" + response2.ReasonPhrase);
                Console.WriteLine("Access Token is invalid !!");
                return;
            }

            Console.WriteLine("Access Token is valid !!");
        }

    }


    class AccessToken
    {
        public string access_token { get; set; }
        public string token_type { get; set; }
    }

    public class Data
    {
        public string app_id { get; set; }
        public string type { get; set; }
        public string application { get; set; }
        public int data_access_expires_at { get; set; }
        public int expires_at { get; set; }
        public bool is_valid { get; set; }
        public List<string> scopes { get; set; }
        public string user_id { get; set; }
    }

    public class ValidateObject
    {
        public Data data { get; set; }
    }
}