💡
OAuth 2.0でTwitter APIを操作するデスクトップアプリ
0. 概要
この記事では、C#とOAuth 2.0を使ってTwitter APIを操作するWindows専用のツイートアプリを作成する方法を解説します。
WPFアプリケーションを使用し、認証フローを実装してアクセストークンを取得する仕組みを学びます。
1. 開発環境のセットアップ
必要な環境
- ツール
- Visual Studio 2022
- .NET 6.0 (LTS)
- アカウント
- Twitter Developer Account を取得して、以下をメモ
- API Key
- API Key Secret
- Bearer Token
- Access Token
- Client ID
- Client Secret
- CallbackURL を入れる欄には "http://127.0.0.1:3000/callback" を入力
- Twitter Developer Account を取得して、以下をメモ
アカウントの設定
Twitter Developer Accountを作って、以下の設定を行う
デフォルトが Read Only の部分を一度 Revoke して Read and Write に変更
2. アプリケーションの構造
プロジェクトの作成
Visual Studioを起動
「新しいプロジェクト」を選択
WPFアプリケーションを開く
.NET 6.0 (長期的なサポート)を選択
UIの構築 (MainWindow.xaml)
MainWindow.xaml
<Window x:Class="TwitterApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TwitterApp" Height="400" Width="400">
<Grid Margin="10">
<StackPanel>
<TextBlock Text="認証コードを入力してください:" Margin="0,10" />
<TextBox Name="CodeInputBox" Width="300" />
<Button Content="アクセストークンを取得" Width="200" Margin="0,10" Click="OnGetAccessTokenClick" />
<TextBlock Text="ツイート内容を入力してください:" Margin="0,20" />
<TextBox Name="TweetInputBox" Width="300" />
<Button Content="ツイートを投稿" Width="200" Margin="0,10" Click="OnPostTweetClick" />
</StackPanel>
</Grid>
</Window>
認証クライアントの実装 (TwitterAuthClient)
MainWindow.xaml.cs
using System;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.Text;
public class TwitterAuthClient
{
private readonly HttpClient _httpClient;
private readonly string _clientId;
private readonly string _clientSecret;
private readonly string _callbackUrl;
public TwitterAuthClient(string clientId, string clientSecret, string callbackUrl)
{
_clientId = clientId;
_clientSecret = clientSecret;
_callbackUrl = callbackUrl;
_httpClient = new HttpClient();
}
public (string Url, string CodeVerifier) GenerateAuthUrl()
{
// PKCEのcode_verifierを生成
string codeVerifier = GenerateCodeVerifier();
string codeChallenge = GenerateCodeChallenge(codeVerifier);
string state = Guid.NewGuid().ToString();
var url = "https://twitter.com/i/oauth2/authorize" +
$"?response_type=code" +
$"&client_id={Uri.EscapeDataString(_clientId)}" +
$"&redirect_uri={Uri.EscapeDataString(_callbackUrl)}" +
$"&scope={Uri.EscapeDataString("tweet.read tweet.write users.read offline.access")}" +
$"&state={Uri.EscapeDataString(state)}" +
$"&code_challenge={Uri.EscapeDataString(codeChallenge)}" +
$"&code_challenge_method=S256";
return (url, codeVerifier);
}
public async Task<string> GetAccessTokenAsync(string code, string codeVerifier)
{
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("client_id", _clientId),
new KeyValuePair<string, string>("client_secret", _clientSecret),
new KeyValuePair<string, string>("code", code),
new KeyValuePair<string, string>("grant_type", "authorization_code"),
new KeyValuePair<string, string>("redirect_uri", _callbackUrl),
new KeyValuePair<string, string>("code_verifier", codeVerifier)
});
var response = await _httpClient.PostAsync("https://api.twitter.com/2/oauth2/token", content);
var responseBody = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
throw new Exception($"Failed to get access token. Status: {response.StatusCode}, Response: {responseBody}");
}
var json = JsonSerializer.Deserialize<JsonElement>(responseBody);
return json.GetProperty("access_token").GetString();
}
private string GenerateCodeVerifier()
{
var bytes = new byte[32];
using (var rng = new RNGCryptoServiceProvider())
{
rng.GetBytes(bytes);
}
return Convert.ToBase64String(bytes)
.TrimEnd('=')
.Replace('+', '-')
.Replace('/', '_');
}
private string GenerateCodeChallenge(string codeVerifier)
{
using (var sha256 = SHA256.Create())
{
var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
return Convert.ToBase64String(challengeBytes)
.TrimEnd('=')
.Replace('+', '-')
.Replace('/', '_');
}
}
}
3. 認証フローの実装
Step 1: 認証URLの生成と認証
これだけでは動かなくて、x.com に許可を得て認証を行う必要がある。
Program.cs
using System;
using System.Diagnostics;
class Program
{
private const string ClientId = "ここにあなたのClientID";
private const string CallbackUrl = "http://127.0.0.1:3000/callback";
static void Main(string[] args)
{
// 認証URLを生成
string authUrl = GenerateAuthUrl();
Console.WriteLine("以下のURLをブラウザで開いて、認証を完了してください:");
Console.WriteLine(authUrl);
// 自動的にブラウザで開く
OpenUrlInBrowser(authUrl);
// 認証コードを入力
Console.WriteLine("認証が完了したら、リダイレクトURLの「code」パラメータをコピーして貼り付けてください:");
string code = Console.ReadLine();
Console.WriteLine($"入力された認証コード: {code}");
Console.WriteLine("このコードを使ってアクセストークンを取得してください。");
// 終了待ち
Console.WriteLine("終了するには何かキーを押してください...");
Console.ReadKey();
}
// 認証URLを生成
private static string GenerateAuthUrl()
{
string state = Guid.NewGuid().ToString();
return $"https://twitter.com/i/oauth2/authorize" +
$"?response_type=code" +
$"&client_id={ClientId}" +
$"&redirect_uri={Uri.EscapeDataString(CallbackUrl)}" +
$"&scope={Uri.EscapeDataString("tweet.read tweet.write users.read offline.access")}" +
$"&state={state}" +
$"&code_challenge=challenge" +
$"&code_challenge_method=plain";
}
// ブラウザでURLを開く
private static void OpenUrlInBrowser(string url)
{
try
{
// Windowsの標準ブラウザで開く
Process.Start(new ProcessStartInfo
{
FileName = url,
UseShellExecute = true
});
}
catch (Exception ex)
{
Console.WriteLine($"ブラウザを開く際にエラーが発生しました: {ex.Message}");
}
}
}
上記のコードを、任意の場所でコンソールを開いて、
dotnet new console
で生まれた Program.cs に貼り付けて
dotnet run
を実行する。
すると x.com の許可を求めるサイトが開く
Step 2: アクセストークンの取得
許可を押すと、接続が切れるものの、URL が
となるので、twaddle をコピー
認証コードの使用
- 30秒以内の制限について
アプリに貼り付けて「アクセストークンを取得」しないと、
認証に失敗する!
うまく認証できていればツイートできる!
まとめ
リフレッシュトークンを使うことで、制限を回避できるらしい
次はそれにチャレンジ
Discussion