OAuth2 (Dart)
認証ライブラリ
OAuth2 (Dart)
概要
OAuth2 (Dart)は、DartとFlutterアプリケーション向けの公式OAuth2クライアントライブラリです。OAuth2プロトコルを使用してユーザーの代理で認証を行い、認可されたHTTPリクエストを作成するための機能を提供します。Authorization Code Grant、Client Credentials Grant、Resource Owner Password Grantフローをサポートし、サーバーサイドアプリケーション(dart:ioを使用)に対応しています。
詳細
OAuth2 (Dart)は、OAuth2.0仕様(RFC 6749)に準拠したDart言語用の認証ライブラリです。Googleが公式に提供するライブラリであり、Dart 3との互換性を確保しています。主な特徴として、透明なトークン管理とリフレッシュ機能、安全なトークンストレージとキャッシュ、PKCE(Proof Key for Code Exchange)のサポートを提供します。
ライブラリは複数のOAuth2フローに対応しており、Authorization Code Grantは最も安全で推奨されるフロー、Client Credentials Grantはサーバー間通信用、Resource Owner Password Grantは信頼できるクライアント用となっています。特にWebアプリケーションとモバイルアプリケーションの両方での使用に適しており、セキュリティベストプラクティスに従った実装となっています。
メリット・デメリット
メリット
- 公式サポート: Googleが公式に提供・保守するライブラリで安定性が高い
- Dart 3対応: 最新のDart言語仕様との完全な互換性
- 包括的なフロー対応: 主要なOAuth2フローを網羅的にサポート
- 透明なトークン管理: アクセストークンとリフレッシュトークンの自動管理
- PKCEサポート: モダンなセキュリティ標準への対応
- サーバーサイド対応:
dart:ioを使用したサーバーサイドアプリケーションに適用可能
デメリット
- サーバーサイド限定: ブラウザ環境での直接使用は制限される
- 設定の複雑さ: 初期設定とプロバイダーごとの設定が複雑になる場合がある
- 依存関係: HTTP通信ライブラリなどの外部依存関係が必要
- ドキュメント: 他の言語に比べて日本語リソースが限定的
参考ページ
書き方の例
基本的な認証フロー
import 'package:oauth2/oauth2.dart' as oauth2;
// OAuth2クライアントの設定
var authorizationEndpoint = Uri.parse('https://example.com/oauth/authorize');
var tokenEndpoint = Uri.parse('https://example.com/oauth/token');
var identifier = 'your-client-id';
var secret = 'your-client-secret';
var redirectUrl = Uri.parse('https://your-app.com/callback');
// Authorization Code Grantフローを使用
var grant = oauth2.AuthorizationCodeGrant(
identifier,
authorizationEndpoint,
tokenEndpoint,
secret: secret,
);
// 認証URLの取得
var authorizationUrl = grant.getAuthorizationUrl(redirectUrl);
print('認証URL: $authorizationUrl');
// コールバック処理後のクライアント取得
var client = await grant.handleAuthorizationResponse(queryParameters);
リフレッシュトークンを使った認証
import 'package:oauth2/oauth2.dart' as oauth2;
// 保存されたクレデンシャルからクライアントを復元
var credentialsJson = getStoredCredentials(); // 何らかの方法で取得
var credentials = oauth2.Credentials.fromJson(credentialsJson);
var client = oauth2.Client(
credentials,
identifier: 'your-client-id',
secret: 'your-client-secret',
);
// 自動的にトークンをリフレッシュして認証済みリクエストを実行
var response = await client.get(Uri.parse('https://api.example.com/user'));
PKCEを使った認証
import 'package:oauth2/oauth2.dart' as oauth2;
import 'package:crypto/crypto.dart';
import 'dart:convert';
import 'dart:math';
// PKCEチャレンジの生成
String generateCodeVerifier() {
var random = Random.secure();
var values = List<int>.generate(32, (i) => random.nextInt(256));
return base64UrlEncode(values).replaceAll('=', '');
}
String generateCodeChallenge(String verifier) {
var bytes = utf8.encode(verifier);
var digest = sha256.convert(bytes);
return base64UrlEncode(digest.bytes).replaceAll('=', '');
}
// PKCE付きの認証フロー
var codeVerifier = generateCodeVerifier();
var codeChallenge = generateCodeChallenge(codeVerifier);
var grant = oauth2.AuthorizationCodeGrant(
identifier,
authorizationEndpoint,
tokenEndpoint,
codeChallenge: codeChallenge,
codeChallengeMethod: 'S256',
);
スコープ指定での認証
import 'package:oauth2/oauth2.dart' as oauth2;
// スコープを指定した認証
var scopes = ['read', 'write', 'admin'];
var authorizationUrl = grant.getAuthorizationUrl(
redirectUrl,
scopes: scopes,
);
print('認証URL(スコープ付き): $authorizationUrl');
エラーハンドリング
import 'package:oauth2/oauth2.dart' as oauth2;
try {
var client = await grant.handleAuthorizationCode('authorization-code');
// 認証済みAPIリクエスト
var response = await client.get(Uri.parse('https://api.example.com/data'));
if (response.statusCode == 200) {
print('APIレスポンス: ${response.body}');
}
} on oauth2.AuthorizationException catch (e) {
print('認証エラー: ${e.error}');
print('詳細: ${e.description}');
} on oauth2.ExpirationException catch (e) {
print('トークン期限切れ: ${e.message}');
// リフレッシュトークンを使って再認証
} catch (e) {
print('その他のエラー: $e');
}
クレデンシャルの永続化
import 'package:oauth2/oauth2.dart' as oauth2;
import 'dart:convert';
import 'dart:io';
// クレデンシャルの保存
void saveCredentials(oauth2.Credentials credentials) {
var credentialsJson = credentials.toJson();
var file = File('credentials.json');
file.writeAsStringSync(jsonEncode(credentialsJson));
}
// クレデンシャルの読み込み
oauth2.Credentials? loadCredentials() {
try {
var file = File('credentials.json');
if (file.existsSync()) {
var credentialsMap = jsonDecode(file.readAsStringSync());
return oauth2.Credentials.fromJson(credentialsMap);
}
} catch (e) {
print('クレデンシャル読み込みエラー: $e');
}
return null;
}
// 保存されたクレデンシャルを使ったクライアント初期化
oauth2.Client? createClientFromStorage() {
var credentials = loadCredentials();
if (credentials != null) {
return oauth2.Client(
credentials,
identifier: 'your-client-id',
secret: 'your-client-secret',
);
}
return null;
}