misskey_auth 0.1.4-beta copy "misskey_auth: ^0.1.4-beta" to clipboard
misskey_auth: ^0.1.4-beta copied to clipboard

Flutter library for Misskey OAuth authentication. Support for MiAuth format is planned in the future.

Misskey Auth #

Demo

License

Language: 🇺🇸 English | 🇯🇵 日本語


English #

A Flutter library for Misskey OAuth authentication with MiAuth support and multi-account token management.

Features #

  • OAuth 2.0 authentication for Misskey servers (v2023.9.0+)
  • MiAuth authentication for older servers
  • External browser authentication (no embedded WebViews)
  • Secure token storage using flutter_secure_storage
  • Cross-platform support (iOS/Android)
  • PKCE (Proof Key for Code Exchange) implementation
  • Custom URL scheme handling for authentication callbacks
  • Multi-account token storage and account switching
  • High-level MisskeyAuthManager to run flows and persist tokens

Installation #

Add this to your package's pubspec.yaml file:

dependencies:
  misskey_auth: ^0.1.4-beta

Quick Start #

1. Set up your client_id page

Misskey's OAuth 2.0 follows the IndieAuth specification. You need:

  • client_id must be a valid URL (e.g., https://yoursite/yourapp/)
  • The HTML hosted at client_id must include the following <link>:
    <link rel="redirect_uri" href="https://yoursite/yourapp/redirect.html">
    
  • The redirect_uri in authorization requests must exactly match the URL in the <link> tag (protocol, case, trailing slash, etc.)
Example HTML page
<!DOCTYPE html>
<html>
<head>
  <title>My App</title>
  <link rel="redirect_uri" href="https://yoursite/yourapp/redirect.html">
</head>
<body>
  <div class="h-app">
    <a href="https://yoursite/yourapp/" class="u-url p-name">Your Misskey App</a>
  </div>
</body>
</html>
Example redirect page
<!DOCTYPE html>
<html>
<body>
    <script>
        const urlParams = new URLSearchParams(window.location.search);
        const code = urlParams.get('code');
        const state = urlParams.get('state');
        const appUrl = `yourscheme://oauth/callback?code=${encodeURIComponent(code)}&state=${encodeURIComponent(state || '')}`;
        window.location.href = appUrl;
    </script>
</body>
</html>
import 'package:misskey_auth/misskey_auth.dart';

// Create manager with default dependencies
final auth = MisskeyAuthManager.defaultInstance();

// OAuth
final oauthKey = await auth.loginWithOAuth(
  MisskeyOAuthConfig(
    host: 'misskey.io',
    clientId: 'https://yourpage/yourapp/',
    redirectUri: 'https://yourpage/yourapp/redirect.html',
    scope: 'read:account write:notes',
    callbackScheme: 'yourscheme',
  ),
  setActive: true,
);

// MiAuth
final miKey = await auth.loginWithMiAuth(
  MisskeyMiAuthConfig(
    host: 'misskey.io',
    appName: 'Your App',
    callbackScheme: 'yourscheme',
    permissions: ['read:account', 'write:notes'],
    iconUrl: 'https://example.com/icon.png',
  ),
  setActive: true,
);

// Tokens
final current = await auth.currentToken();
final specific = await auth.tokenOf(oauthKey);

// Accounts
final accounts = await auth.listAccounts();
await auth.setActive(miKey);
await auth.signOut(oauthKey);
await auth.signOutAll();

3. Platform Configuration

iOS Configuration

Add to ios/Runner/Info.plist:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>com.yourcompany.yourapp</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>yourscheme</string>
        </array>
    </dict>
</array>
Android Configuration

Add to android/app/src/main/AndroidManifest.xml:

<activity android:name="com.linusu.flutter_web_auth.CallbackActivity" android:exported="true">
    <intent-filter android:label="flutter_web_auth_2">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="yourscheme" />
    </intent-filter>
</activity>

Notes:

  • The android:label attribute on the <intent-filter> is optional. You can omit it or set any string.
  • On Android 12+ (API level 31+), any Activity with an intent-filter must declare android:exported="true".
  • Use the same callback scheme string on both platforms: iOS (CFBundleURLSchemes) and Android (<data android:scheme="...">). They must match exactly.

Differences in MiAuth and OAuth Configuration (Key Points for App Integration)

  • This configuration (registration of the URL scheme) is done on the "app side." It is not included in the library's Manifest.
  • Both methods require a "custom URL scheme" to return from an external browser to the app.
  • The difference lies in how to specify "where to return from the browser."
  • OAuth: Since it needs to return to an HTTPS redirect_uri from the authorization server, redirect.html placed there ultimately redirects back to yourscheme://... for the app.
  • MiAuth: The callback query of the authentication start URL points to the app via the custom scheme only (e.g., yourscheme://). No https is needed.
Example of MiAuth (no persistence)
import 'package:misskey_auth/misskey_auth.dart';

final miClient = MisskeyMiAuthClient(); // does not save tokens
final miConfig = MisskeyMiAuthConfig(
  host: 'misskey.io',
  appName: 'Your App',
  callbackScheme: 'yourscheme',          // Scheme registered on the app side
  permissions: ['read:account', 'write:notes'],
  iconUrl: 'https://example.com/icon.png', // Optional
);
final miRes = await miClient.authenticate(miConfig); // returns token (and user if available)
Example of MiAuth (with persistence via MisskeyAuthManager)
import 'package:misskey_auth/misskey_auth.dart';

final auth = MisskeyAuthManager.defaultInstance();
final key = await auth.loginWithMiAuth(
  MisskeyMiAuthConfig(
    host: 'misskey.io',
    appName: 'Your App',
    callbackScheme: 'yourscheme',
    permissions: ['read:account', 'write:notes'],
    iconUrl: 'https://example.com/icon.png',
  ),
  setActive: true, // also mark as active account
);
// Token is saved via SecureTokenStore; you can read it later:
final current = await auth.currentToken();
Example of OAuth (no persistence)
import 'package:misskey_auth/misskey_auth.dart';

final oauthClient = MisskeyOAuthClient(); // does not save tokens
final oauthConfig = MisskeyOAuthConfig(
  host: 'misskey.io',
  clientId: 'https://yourpage/yourapp/',
  redirectUri: 'https://yourpage/yourapp/redirect.html',
  scope: 'read:account write:notes',
  callbackScheme: 'yourscheme',          // Scheme registered on the app side
);
final token = await oauthClient.authenticate(oauthConfig); // returns token only
Example of OAuth (with persistence via MisskeyAuthManager)
import 'package:misskey_auth/misskey_auth.dart';

final auth = MisskeyAuthManager.defaultInstance();
final key = await auth.loginWithOAuth(
  MisskeyOAuthConfig(
    host: 'misskey.io',
    clientId: 'https://yourpage/yourapp/',
    redirectUri: 'https://yourpage/yourapp/redirect.html',
    scope: 'read:account write:notes',
    callbackScheme: 'yourscheme',
  ),
  setActive: true,
);
// Token is saved via SecureTokenStore; you can read it later:
final current = await auth.currentToken();
How to Support Both Methods in the Same App
  • By registering the same scheme (e.g., yourscheme) in iOS's Info.plist and Android's AndroidManifest.xml, it can be shared between OAuth and MiAuth.
  • This library uses a scheme-only callback for MiAuth (e.g., yourscheme://). You do not need to reuse a path like yourscheme://oauth/callback for MiAuth.
  • For Android, matching only on the scheme is sufficient as shown below (the host and path are optional).
<activity android:name="com.linusu.flutter_web_auth.CallbackActivity" android:exported="true">
    <intent-filter android:label="flutter_web_auth_2">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="yourscheme" />
    </intent-filter>
    <!-- Add this only if you want to restrict by host/path -->
    <!-- <intent-filter> ... <data android:scheme="yourscheme" android:host="oauth" android:path="/callback"/> ... </intent-filter> -->
  </activity>

API Reference #

MisskeyOAuthConfig

Configuration class for Misskey OAuth authentication.

class MisskeyOAuthConfig {
  final String host;           // Misskey server host (e.g., 'misskey.io')
  final String clientId;       // Your client_id page URL
  final String redirectUri;    // Your redirect page URL
  final String scope;          // Requested scopes (e.g., 'read:account write:notes')
  final String callbackScheme; // Your app's custom URL scheme
}

MisskeyOAuthClient

Main client for handling Misskey OAuth authentication.

class MisskeyOAuthClient {
  /// Authenticate with Misskey server (no persistence)
  Future<OAuthTokenResponse?> authenticate(MisskeyOAuthConfig config);
  
  /// Get OAuth server information
  Future<OAuthServerInfo?> getOAuthServerInfo(String host);
}

MisskeyMiAuthClient

Main client for handling Misskey MiAuth authentication.

class MisskeyMiAuthClient {
  /// Authenticate with Misskey server using MiAuth (no persistence)
  Future<MiAuthTokenResponse> authenticate(MisskeyMiAuthConfig config);
}

MisskeyAuthManager

High-level API to run OAuth/MiAuth and persist tokens via TokenStore. The default defaultInstance() uses SecureTokenStore.

class MisskeyAuthManager {
  static MisskeyAuthManager defaultInstance();

  Future<AccountKey> loginWithOAuth(MisskeyOAuthConfig config, { bool setActive = true });
  Future<AccountKey> loginWithMiAuth(MisskeyMiAuthConfig config, { bool setActive = true });

  Future<StoredToken?> currentToken();
  Future<StoredToken?> tokenOf(AccountKey key);

  Future<void> setActive(AccountKey key);
  Future<AccountKey?> getActive();
  Future<void> clearActive();

  Future<List<AccountEntry>> listAccounts();
  Future<void> signOut(AccountKey key);
  Future<void> signOutAll();
}

TokenStore / SecureTokenStore

abstract class TokenStore {
  Future<void> upsert(AccountKey key, StoredToken token);
  Future<StoredToken?> read(AccountKey key);
  Future<List<AccountEntry>> list();
  Future<void> delete(AccountKey key);
  Future<void> clearAll();
  Future<void> setActive(AccountKey? key);
  Future<AccountKey?> getActive();
}

Models (excerpt)

class StoredToken {
  final String accessToken;
  final String tokenType; // 'MiAuth' | 'OAuth'
  final String? scope;
  final Map<String, dynamic>? user;
  final DateTime? createdAt;
}

class AccountKey {
  final String host;
  final String accountId;
}

class AccountEntry {
  final AccountKey key;
  final String? userName;
  final DateTime? createdAt;
}

Error Handling #

The library provides comprehensive error handling with custom exception classes for different scenarios. For detailed information about each exception class and their usage, please refer to the documentation on pub.flutter-io.cn.

The library includes exception classes for:

  • Authentication configuration errors
  • Network and connectivity issues
  • OAuth and MiAuth specific errors
  • User cancellation and authorization failures
  • Secure storage operations
  • Response parsing errors

Common Errors #

  • Invalid redirect_uri: The redirect_uri in the authorization request doesn't exactly match the one in the client_id page's <link rel="redirect_uri"> tag
    • Check domain case, trailing slashes, and HTTPS usage

License #

This project is published by 司書 (LibraryLibrarian) under the 3-Clause BSD License. For details, please see the LICENSE file.


Japanese #

MisskeyのOAuth認証・MiAuth認証に加え、マルチアカウントのトークン管理を提供するFlutterライブラリ。

内容 #

  • MisskeyサーバーのOAuth 2.0認証対応(v2023.9.0以降)
  • 古いサーバー向けMiAuth認証
  • 埋め込みWebViewを使用しない認証
  • flutter_secure_storage を使用したセキュアなトークン保存
  • クロスプラットフォーム対応(iOS/Android)
  • PKCE(Proof Key for Code Exchange)実装
  • 認証コールバック用カスタムURLスキーム対応
  • マルチアカウントのトークン保存とアカウント切替
  • 認証と保存を仲介する高レベルAPI MisskeyAuthManager

インストール #

pubspec.yamlファイルに以下を追加してください:

dependencies:
  misskey_auth: ^0.1.4-beta

クイックスタート #

1. client_idページの設定

MisskeyのOAuth 2.0はIndieAuth仕様に準拠しています。以下が必要です:

  • client_idは有効なURLであること(例: https://yoursite/yourapp/
  • client_idでホストしているHTMLに、以下の<link>を含めること:
    <link rel="redirect_uri" href="https://yoursite/yourapp/redirect.html">
    
  • 認可リクエストのredirect_uriが、上記<link>のURLと完全一致すること(プロトコル、大文字小文字、末尾スラッシュまで一致)
HTMLページ例
<!DOCTYPE html>
<html>
<head>
  <title>My App</title>
  <link rel="redirect_uri" href="https://yoursite/yourapp/redirect.html">
</head>
<body>
  <div class="h-app">
    <a href="https://yoursite/yourapp/" class="u-url p-name">Your Misskey App</a>
  </div>
</body>
</html>
リダイレクトページ例
<!DOCTYPE html>
<html>
<body>
    <script>
        const urlParams = new URLSearchParams(window.location.search);
        const code = urlParams.get('code');
        const state = urlParams.get('state');
        const appUrl = `yourscheme://oauth/callback?code=${encodeURIComponent(code)}&state=${encodeURIComponent(state || '')}`;
        window.location.href = appUrl;
    </script>
</body>
</html>

2. 基本的な認証(推奨: MisskeyAuthManager 経由)

import 'package:misskey_auth/misskey_auth.dart';

final auth = MisskeyAuthManager.defaultInstance();

// OAuth
final oauthKey = await auth.loginWithOAuth(
  MisskeyOAuthConfig(
    host: 'misskey.io',
    clientId: 'https://yourpage/yourapp/',
    redirectUri: 'https://yourpage/yourapp/redirect.html',
    scope: 'read:account write:notes',
    callbackScheme: 'yourscheme',
  ),
  setActive: true,
);

// MiAuth
final miKey = await auth.loginWithMiAuth(
  MisskeyMiAuthConfig(
    host: 'misskey.io',
    appName: 'Your App',
    callbackScheme: 'yourscheme',
    permissions: ['read:account', 'write:notes'],
    iconUrl: 'https://example.com/icon.png',
  ),
  setActive: true,
);

// トークン取得
final current = await auth.currentToken();
final specific = await auth.tokenOf(oauthKey);

// アカウント管理
final accounts = await auth.listAccounts();
await auth.setActive(miKey);
await auth.signOut(oauthKey);
await auth.signOutAll();

3. プラットフォーム設定

iOS設定

ios/Runner/Info.plistに追加:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>com.yourcompany.yourapp</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>yourscheme</string>
        </array>
    </dict>
</array>
Android設定

android/app/src/main/AndroidManifest.xmlに追加:

<activity android:name="com.linusu.flutter_web_auth.CallbackActivity" android:exported="true">
    <intent-filter android:label="flutter_web_auth_2">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="yourscheme" />
    </intent-filter>
</activity>

補足:

  • <intent-filter>android:label は省略可能です(省略しても動作します)。
  • Android 12+(API 31 以降)では、intent-filter を持つ Activity に android:exported="true" の指定が必須です。
  • iOS(CFBundleURLSchemes)と Android(<data android:scheme="...">)で登録するカスタムスキーム名は同一にしてください(完全一致が必要)。

MiAuth と OAuth の設定の違い(アプリ組み込み時のポイント)

  • この設定(URLスキームの登録)は「アプリ側」で行います。ライブラリ内のManifestには含めません。
  • 両方式とも、外部ブラウザからアプリへ戻すために「カスタムURLスキーム」が必要です。
  • 相違点は「ブラウザからどこに戻すか」の指定方法です。
    • OAuth: 認可サーバーからはHTTPSのredirect_uriに戻る必要があるため、そこに配置したredirect.htmlが最終的にyourscheme://...へリダイレクトしてアプリに戻します。
    • MiAuth: 認証開始URLのcallbackクエリには、アプリのカスタムスキームのみ(例: yourscheme://)を指定します(httpsは不要)。
MiAuth の例(保存無し)
import 'package:misskey_auth/misskey_auth.dart';

final miClient = MisskeyMiAuthClient(); // 保存はしません
final miConfig = MisskeyMiAuthConfig(
  host: 'misskey.io',
  appName: 'Your App',
  callbackScheme: 'yourscheme',          // アプリ側で登録したスキーム
  permissions: ['read:account', 'write:notes'],
  iconUrl: 'https://example.com/icon.png', // 任意
);
final miRes = await miClient.authenticate(miConfig); // トークン(必要に応じて user も)を返します
MiAuth の例(MisskeyAuthManager による保存あり)
import 'package:misskey_auth/misskey_auth.dart';

final auth = MisskeyAuthManager.defaultInstance();
final key = await auth.loginWithMiAuth(
  MisskeyMiAuthConfig(
    host: 'misskey.io',
    appName: 'Your App',
    callbackScheme: 'yourscheme',
    permissions: ['read:account', 'write:notes'],
    iconUrl: 'https://example.com/icon.png',
  ),
  setActive: true,
);
// トークンは SecureTokenStore に保存され、後から取得できます
final current = await auth.currentToken();
OAuth の例(保存無し)
import 'package:misskey_auth/misskey_auth.dart';

final oauthClient = MisskeyOAuthClient(); // 保存はしません
final oauthConfig = MisskeyOAuthConfig(
  host: 'misskey.io',
  clientId: 'https://yourpage/yourapp/',
  redirectUri: 'https://yourpage/yourapp/redirect.html',
  scope: 'read:account write:notes',
  callbackScheme: 'yourscheme',          // アプリ側で登録したスキーム
);
final token = await oauthClient.authenticate(oauthConfig); // トークンのみ返します
OAuth の例(MisskeyAuthManager による保存あり)
import 'package:misskey_auth/misskey_auth.dart';

final auth = MisskeyAuthManager.defaultInstance();
final key = await auth.loginWithOAuth(
  MisskeyOAuthConfig(
    host: 'misskey.io',
    clientId: 'https://yourpage/yourapp/',
    redirectUri: 'https://yourpage/yourapp/redirect.html',
    scope: 'read:account write:notes',
    callbackScheme: 'yourscheme',
  ),
  setActive: true,
);
// トークンは SecureTokenStore に保存され、後から取得できます
final current = await auth.currentToken();
両方式を同一アプリでサポートするには
  • iOSのInfo.plist・AndroidのAndroidManifest.xmlで同じscheme(例: yourscheme)を1つ登録すれば、OAuth/MiAuthで共用可能です。
  • 本ライブラリの MiAuth は scheme のみ(yourscheme://)を callback に使います。yourscheme://oauth/callback のようなパス付きに揃える必要はありません。
  • Androidは以下のようにschemeのみのマッチで十分です(hostpathは任意)。
<activity android:name="com.linusu.flutter_web_auth.CallbackActivity" android:exported="true">
    <intent-filter android:label="flutter_web_auth_2">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="yourscheme" />
    </intent-filter>
    <!-- 必要に応じて、host/pathで限定したい場合のみ追記 -->
    <!-- <intent-filter> ... <data android:scheme="yourscheme" android:host="oauth" android:path="/callback"/> ... </intent-filter> -->
  </activity>

API リファレンス #

MisskeyOAuthConfig

Misskey OAuth認証の設定クラス。

class MisskeyOAuthConfig {
  final String host;           // Misskeyサーバーのホスト(例: 'misskey.io')
  final String clientId;       // client_idページのURL
  final String redirectUri;    // リダイレクトページのURL
  final String scope;          // 要求するスコープ(例: 'read:account write:notes')
  final String callbackScheme; // アプリのカスタムURLスキーム
}

MisskeyOAuthClient

Misskey OAuth認証を処理するメインクラス

class MisskeyOAuthClient {
  /// Misskeyサーバーで認証を実行(保存は行いません)
  Future<OAuthTokenResponse?> authenticate(MisskeyOAuthConfig config);
  
  /// OAuthサーバー情報を取得
  Future<OAuthServerInfo?> getOAuthServerInfo(String host);
}

MisskeyMiAuthClient

Misskey MiAuth認証を処理するメインクラス

class MisskeyMiAuthClient {
  /// MisskeyサーバーでMiAuth認証を実行(Tokenの保存はされません)
  Future<MiAuthTokenResponse> authenticate(MisskeyMiAuthConfig config);
}

#### MisskeyAuthManager

`TokenStore` を介して OAuth/MiAuth を実行し、トークンを保存する高レベルAPI。

```dart
class MisskeyAuthManager {
  static MisskeyAuthManager defaultInstance();

  Future<AccountKey> loginWithOAuth(MisskeyOAuthConfig config, { bool setActive = true });
  Future<AccountKey> loginWithMiAuth(MisskeyMiAuthConfig config, { bool setActive = true });

  Future<StoredToken?> currentToken();
  Future<StoredToken?> tokenOf(AccountKey key);

  Future<void> setActive(AccountKey key);
  Future<AccountKey?> getActive();
  Future<void> clearActive();

  Future<List<AccountEntry>> listAccounts();
  Future<void> signOut(AccountKey key);
  Future<void> signOutAll();
}

TokenStore / SecureTokenStore

abstract class TokenStore {
  Future<void> upsert(AccountKey key, StoredToken token);
  Future<StoredToken?> read(AccountKey key);
  Future<List<AccountEntry>> list();
  Future<void> delete(AccountKey key);
  Future<void> clearAll();
  Future<void> setActive(AccountKey? key);
  Future<AccountKey?> getActive();
}

モデル(抜粋)

class StoredToken {
  final String accessToken;
  final String tokenType; // 'MiAuth' | 'OAuth'
  final String? scope;
  final Map<String, dynamic>? user;
  final DateTime? createdAt;
}

class AccountKey {
  final String host;
  final String accountId;
}

class AccountEntry {
  final AccountKey key;
  final String? userName;
  final DateTime? createdAt;
}

エラーハンドリング #

ライブラリには以下のカテゴリの例外クラスが含まれています:

  • 認証設定エラー
  • ネットワーク・接続エラー
  • OAuth・MiAuth固有のエラー
  • ユーザーキャンセル・認可失敗
  • セキュアストレージ操作エラー
  • レスポンス解析エラー

詳細についてはpub.flutter-io.cnのドキュメントを参考にして下さい

よくあるエラー #

  • Invalid redirect_uri: 認可リクエストのredirect_uriと、client_idページの<link rel="redirect_uri">が完全一致していない
    • ドメインの大文字小文字、末尾スラッシュ、HTTPS使用を確認してください

ライセンス #

このプロジェクトは司書(LibraryLibrarian)によって、3-Clause BSD Licenseの下で公開されています。詳細はLICENSEファイルをご覧ください。

リンク #

0
likes
160
points
37
downloads

Publisher

verified publisherlibrarylibrarian.com

Weekly Downloads

Flutter library for Misskey OAuth authentication. Support for MiAuth format is planned in the future.

Homepage
Repository (GitHub)
View/report issues

Topics

#authentication #oauth #misskey #social-media #api

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

crypto, cupertino_icons, dio, flutter, flutter_secure_storage, flutter_web_auth_2

More

Packages that depend on misskey_auth