본문으로 건너뛰기

Flutter 애플리케이션에 인증 (Authentication)을 추가하세요

이 튜토리얼은 Flutter 애플리케이션에 Logto를 통합하는 방법을 보여줍니다.

:
  • SDK 패키지는 pub.dev와 Logto GitHub 저장소에서 사용할 수 있습니다.
  • 샘플 프로젝트는 Flutter material을 사용하여 구축되었습니다. pub.dev에서 찾을 수 있습니다.
  • 이 SDK는 iOS, Android 및 웹 플랫폼의 Flutter 애플리케이션과 호환됩니다. 다른 플랫폼과의 호환성은 테스트되지 않았습니다.

사전 준비 사항

설치

logto_dart_sdk 패키지를 pub 패키지 관리자를 사용하여 직접 설치할 수 있습니다. 프로젝트 루트에서 다음 명령을 실행하세요:

flutter pub add logto_dart_sdk

또는 pubspec.yaml 파일에 다음을 추가하세요:

dependencies:
logto_dart_sdk: ^3.0.0

그런 다음 다음을 실행하세요:

flutter pub get

설정

SDK 버전 호환성

Logto SDK 버전Dart SDK 버전Dart 3.0 호환 가능
< 2.0.0>= 2.17.6 < 3.0.0false
>= 2.0.0 < 3.0.0>= 3.0.0true
>= 3.0.0>= 3.6.0true

flutter_secure_storage 설정

이 SDK는 flutter_secure_storage를 사용하여 크로스 플랫폼 지속적 보안 토큰 저장소를 구현합니다.

  • iOS에서는 Keychain이 사용됩니다.
  • Android에서는 AES 암호화가 사용됩니다.

Android 버전 구성

프로젝트의 android/app/build.gradle 파일에서 android:minSdkVersion>= 18로 설정하세요.

build.gradle
  android {
...

defaultConfig {
...
minSdkVersion 18
...
}
}

Android에서 자동 백업 비활성화

기본적으로 Android는 Google Drive에 데이터를 백업합니다. 이는 java.security.InvalidKeyException:Failed 예외를 발생시킬 수 있습니다. 이를 방지하려면,

  1. 자동 백업을 비활성화하려면 앱 매니페스트 파일로 이동하여 android:allowBackupandroid:fullBackupContent 속성을 false로 설정하세요.

    AndroidManifest.xml
    <manifest ... >
    ...
    <application
    android:allowBackup="false"
    android:fullBackupContent="false"
    ...
    >
    ...
    </application>
    </manifest>

  2. FlutterSecureStorage에서 sharedprefs를 제외하세요.

    앱에 대해 android:fullBackupContent를 비활성화하지 않고 유지해야 하는 경우, 백업에서 sharedprefs 디렉토리를 제외할 수 있습니다. 자세한 내용은 Android 문서를 참조하세요.

    AndroidManifest.xml 파일에서 <application> 요소에 android:fullBackupContent 속성을 추가하세요. 이 속성은 백업 규칙을 포함하는 XML 파일을 가리킵니다.

    AndroidManifest.xml
    <application ...
    android:fullBackupContent="@xml/backup_rules">
    </application>

    res/xml/ 디렉토리에 @xml/backup_rules라는 XML 파일을 생성하세요. 이 파일에서 <include><exclude> 요소로 규칙을 추가하세요. 다음 샘플은 device.xml을 제외한 모든 공유 환경설정을 백업합니다:

    @xml/backup_rules
    <?xml version="1.0" encoding="utf-8"?>
    <full-backup-content>
    <exclude domain="sharedpref" path="FlutterSecureStorage"/>
    </full-backup-content>

자세한 내용은 flutter_secure_storage를 확인하세요.

flutter_web_auth_2 설정

이 SDK는 flutter_web_auth_2를 사용하여 Logto로 사용자를 인증합니다. 이 패키지는 시스템 웹뷰 또는 브라우저를 사용하여 Logto로 사용자를 간단하게 인증하는 방법을 제공합니다.

이 플러그인은 iOS 12+ 및 macOS 10.15+에서 ASWebAuthenticationSession, iOS 11에서 SFAuthenticationSession, Android에서 Chrome Custom Tabs를 사용하며 웹에서는 새 창을 엽니다.

  • iOS: 추가 설정 필요 없음

  • Android: Android에서 콜백 URL 등록

    Logto의 로그인 웹 페이지에서 콜백 URL을 캡처하려면, AndroidManifest.xml 파일에 로그인 redirectUri를 등록해야 합니다.

    AndroidManifest.xml
      <manifest>
    <application>
    <activity
    android:name="com.linusu.flutter_web_auth_2.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="YOUR_CALLBACK_URL_SCHEME_HERE" />
    </intent-filter>
    </activity>
    </application>
    </manifest>
  • 웹 브라우저: 콜백 URL을 처리할 엔드포인트 생성

    웹 플랫폼을 사용하는 경우, 콜백 URL을 처리하고 postMessage API를 사용하여 애플리케이션으로 다시 보내는 엔드포인트를 생성해야 합니다.

    callback.html
    <!doctype html>
    <title>인증 완료</title>
    <p>인증이 완료되었습니다. 자동으로 진행되지 않으면 창을 닫아주세요.</p>
    <script>
    function postAuthenticationMessage() {
    const message = {
    'flutter-web-auth-2': window.location.href,
    };

    if (window.opener) {
    window.opener.postMessage(message, window.location.origin);
    window.close();
    } else if (window.parent && window.parent !== window) {
    window.parent.postMessage(message, window.location.origin);
    } else {
    localStorage.setItem('flutter-web-auth-2', window.location.href);
    window.close();
    }
    }

    postAuthenticationMessage();
    </script>

자세한 내용은 flutter_web_auth_2 패키지의 설정 가이드를 확인하세요.

통합

LogtoClient 초기화

logto_dart_sdk 패키지를 가져와 애플리케이션의 루트에서 LogtoClient 인스턴스를 초기화하세요.

lib/main.dart
import 'package:logto_dart_sdk/logto_dart_sdk.dart';
import 'package:http/http.dart' as http;

void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);


Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(title: 'Logto Demo Home Page'),
);
}
}

class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;


State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
late LogtoClient logtoClient;

void render() {
// 상태 변경
}

// LogtoConfig
final logtoConfig = const LogtoConfig(
endpoint: "<your-logto-endpoint>",
appId: "<your-app-id>"
);

void _init() {
logtoClient = LogtoClient(
config: logtoConfig,
httpClient: http.Client(), // 선택적 http 클라이언트
);
render();
}


void initState() {
super.initState();
_init();
}

// ...
}

로그인 구현

세부 사항을 살펴보기 전에, 최종 사용자 경험에 대한 간단한 개요를 소개합니다. 로그인 과정은 다음과 같이 간소화될 수 있습니다:

  1. 귀하의 앱이 로그인 메서드를 호출합니다.
  2. 사용자는 Logto 로그인 페이지로 리디렉션됩니다. 네이티브 앱의 경우, 시스템 브라우저가 열립니다.
  3. 사용자가 로그인하고 귀하의 앱으로 다시 리디렉션됩니다 (리디렉션 URI로 구성됨).

리디렉션 기반 로그인에 관하여

  1. 이 인증 과정은 OpenID Connect (OIDC) 프로토콜을 따르며, Logto는 사용자 로그인을 보호하기 위해 엄격한 보안 조치를 시행합니다.
  2. 여러 앱이 있는 경우, 동일한 아이덴티티 제공자 (Logto)를 사용할 수 있습니다. 사용자가 한 앱에 로그인하면, Logto는 사용자가 다른 앱에 접근할 때 자동으로 로그인 과정을 완료합니다.

리디렉션 기반 로그인에 대한 이론적 배경과 이점에 대해 더 알고 싶다면, Logto 로그인 경험 설명을 참조하세요.


시작하기 전에, 애플리케이션의 Admin Console에서 리디렉션 URI를 추가해야 합니다.

Logto 콘솔의 애플리케이션 세부 정보 페이지로 전환하세요. 리디렉션 URI io.logto://callback를 추가하고 "변경 사항 저장"을 클릭하세요.

Logto 콘솔의 리디렉션 URI
  • iOS의 경우, ASWebAuthenticationSession 클래스가 등록 여부와 상관없이 리디렉션 URI를 수신하므로 리디렉션 URI 스킴은 중요하지 않습니다.
  • Android의 경우, 리디렉션 URI 스킴은 AndroidManifest.xml 파일에 등록되어야 합니다.

리디렉션 URI가 구성된 후, 페이지에 로그인 버튼을 추가하여 Logto 로그인 흐름을 호출하는 logtoClient.signIn API를 호출합니다:

lib/main.dart
class _MyHomePageState extends State<MyHomePage> {
// ...
final redirectUri = 'io.logto://callback';


Widget build(BuildContext context) {
// ...

Widget signInButton = TextButton(
onPressed: () async {
await logtoClient.signIn(redirectUri);
render();
},
child: const Text('Sign In'),
);

return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SelectableText('My Demo App'),
signInButton,
],
),
),
);
}
}

로그아웃 구현

Logto Console의 애플리케이션 세부 정보 페이지로 전환하세요. 로그아웃 후 리디렉션 URI io.logto://callback를 추가하고 "변경 사항 저장"을 클릭하세요.

Logto Console의 로그아웃 후 리디렉션 URI

로그아웃 후 리디렉션 URI는 OAuth 2.0 개념으로, 로그아웃 후 리디렉션할 위치를 의미합니다.

이제 메인 페이지에 로그아웃 버튼을 추가하여 사용자가 애플리케이션에서 로그아웃할 수 있도록 합니다.

lib/main.dart
class _MyHomePageState extends State<MyHomePage> {
// ...

final postSignOutRedirectUri = 'io.logto//home';


Widget build(BuildContext context) {
// ...

Widget signOutButton = TextButton(
onPressed: () async {
await logtoClient.signOut(postSignOutRedirectUri);
render();
},
child: const Text('Sign Out'),
);

return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SelectableText('My Demo App'),
signInButton,
signOutButton,
],
),
),
);
}
}

인증 상태 처리

Logto SDK는 인증 상태를 확인하는 비동기 메서드를 제공합니다. 이 메서드는 logtoClient.isAuthenticated입니다. 이 메서드는 사용자가 인증되었을 경우 true, 그렇지 않으면 false를 반환합니다.

예제에서는 인증 상태에 따라 로그인 및 로그아웃 버튼을 조건부로 렌더링합니다. 이제 위젯의 render 메서드를 업데이트하여 상태 변경을 처리합시다:

lib/main.dart
class _MyHomePageState extends State<MyHomePage> {
// ...
bool? isAuthenticated = false;

void render() {
setState(() async {
isAuthenticated = await logtoClient.isAuthenticated;
});
}


Widget build(BuildContext context) {
// ...

return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SelectableText('My Demo App'),
isAuthenticated == true ? signOutButton : signInButton,
],
),
),
);
}
}

체크포인트: 애플리케이션 테스트하기

이제 애플리케이션을 테스트할 수 있습니다:

  1. 애플리케이션을 실행하면 로그인 버튼이 표시됩니다.
  2. 로그인 버튼을 클릭하면 SDK가 로그인 프로세스를 초기화하고 Logto 로그인 페이지로 리디렉션됩니다.
  3. 로그인 후, 애플리케이션으로 다시 리디렉션되어 로그아웃 버튼이 표시됩니다.
  4. 로그아웃 버튼을 클릭하여 토큰 저장소를 지우고 로그아웃합니다.

사용자 정보 가져오기

사용자 정보 표시

사용자의 정보를 표시하려면 logtoClient.idTokenClaims getter를 사용할 수 있습니다. 예를 들어, Flutter 앱에서:

lib/main.dart
class _MyHomePageState extends State<MyHomePage> {
// ...


Widget build(BuildContext context) {
// ...

Widget getUserInfoButton = TextButton(
onPressed: () async {
final userClaims = await logtoClient.idTokenClaims;
print(userInfo);
},
child: const Text('Get user info'),
);

return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SelectableText('My Demo App'),
isAuthenticated == true ? signOutButton : signInButton,
isAuthenticated == true ? getUserInfoButton : const SizedBox.shrink(),
],
),
),
);
}
}

추가 클레임 요청

client.idTokenClaims에서 반환된 객체에 일부 사용자 정보가 누락된 것을 발견할 수 있습니다. 이는 OAuth 2.0 및 OpenID Connect (OIDC)가 최소 권한 원칙 (PoLP)을 따르도록 설계되었기 때문이며, Logto는 이러한 표준을 기반으로 구축되었습니다.

기본적으로 제한된 클레임 (Claim)만 반환됩니다. 더 많은 정보를 원하시면, 추가적인 스코프 (Scope)를 요청하여 더 많은 클레임에 접근할 수 있습니다.

정보:

"클레임 (Claim)"은 주체에 대해 주장하는 내용이며, "스코프 (Scope)"는 클레임의 그룹입니다. 현재의 경우, 클레임은 사용자에 대한 정보입니다.

다음은 스코프 - 클레임 관계의 비규범적 예시입니다:

:

"sub" 클레임은 "주체"를 의미하며, 이는 사용자의 고유 식별자 (즉, 사용자 ID)입니다.

Logto SDK는 항상 세 가지 스코프를 요청합니다: openid, profile, 그리고 offline_access.

추가 스코프를 요청하려면, 스코프를 LogtoConfig 객체에 전달할 수 있습니다. 예를 들어:

lib/main.dart
// LogtoConfig
final logtoConfig = const LogtoConfig(
endpoint: "<your-logto-endpoint>",
appId: "<your-app-id>",
scopes: ["email", "phone"],
);

SDK 패키지에는 미리 정의된 스코프를 사용할 수 있도록 도와주는 내장 LogtoUserScope 열거형도 제공합니다.

// LogtoConfig
final logtoConfig = const LogtoConfig(
endpoint: "<your-logto-endpoint>",
appId: "<your-app-id>",
scopes: [LogtoUserScope.email.value, LogtoUserScope.phone.value],
);

네트워크 요청이 필요한 클레임

ID 토큰의 비대화를 방지하기 위해, 일부 클레임은 네트워크 요청을 통해 가져와야 합니다. 예를 들어, custom_data 클레임은 스코프에서 요청되더라도 사용자 객체에 포함되지 않습니다. 이러한 클레임에 접근하려면, logtoClient.getUserInfo() 메서드를 사용할 수 있습니다:

lib/main.dart
class _MyHomePageState extends State<MyHomePage> {
// ...


Widget build(BuildContext context) {
// ...

Widget getUserInfoButton = TextButton(
onPressed: () async {
final userInfo = await logtoClient.getUserInfo();
print(userInfo);
},
child: const Text('Get user info'),
);

return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SelectableText('My Demo App'),
isAuthenticated == true ? signOutButton : signInButton,
isAuthenticated == true ? getUserInfoButton : const SizedBox.shrink(),
],
),
),
);
}
}
이 메서드는 userinfo 엔드포인트에 요청하여 사용자 정보를 가져옵니다. 사용 가능한 스코프와 클레임에 대해 더 알고 싶다면, 스코프와 클레임 섹션을 참조하세요.

스코프와 클레임

Logto는 OIDC 스코프 및 클레임 규약을 사용하여 ID 토큰 및 OIDC userinfo 엔드포인트에서 사용자 정보를 가져오기 위한 스코프와 클레임을 정의합니다. "스코프"와 "클레임"은 OAuth 2.0 및 OpenID Connect (OIDC) 사양의 용어입니다.

지원되는 스코프와 해당 클레임 (Claims) 목록은 다음과 같습니다:

openid

클레임 이름유형설명사용자 정보 필요 여부
substring사용자의 고유 식별자아니요

profile

클레임 이름유형설명사용자 정보 필요 여부
namestring사용자의 전체 이름아니요
usernamestring사용자의 사용자 이름아니요
picturestring최종 사용자의 프로필 사진 URL. 이 URL은 이미지 파일 (예: PNG, JPEG, 또는 GIF 이미지 파일)을 참조해야 하며, 이미지를 포함하는 웹 페이지를 참조해서는 안 됩니다. 이 URL은 최종 사용자를 설명할 때 표시하기 적합한 프로필 사진을 구체적으로 참조해야 하며, 최종 사용자가 찍은 임의의 사진을 참조해서는 안 됩니다.아니요
created_atnumber최종 사용자가 생성된 시간. 시간은 유닉스 에포크 (1970-01-01T00:00:00Z) 이후 밀리초 수로 표현됩니다.아니요
updated_atnumber최종 사용자의 정보가 마지막으로 업데이트된 시간. 시간은 유닉스 에포크 (1970-01-01T00:00:00Z) 이후 밀리초 수로 표현됩니다.아니요

다른 표준 클레임family_name, given_name, middle_name, nickname, preferred_username, profile, website, gender, birthdate, zoneinfo, 및 locale도 사용자 정보 엔드포인트를 요청할 필요 없이 profile 스코프에 포함됩니다. 위의 클레임과의 차이점은 이러한 클레임은 값이 비어 있지 않을 때만 반환되며, 위의 클레임은 값이 비어 있을 경우 null을 반환한다는 점입니다.

노트:

표준 클레임과 달리, created_atupdated_at 클레임은 초 대신 밀리초를 사용합니다.

email

클레임 이름유형설명사용자 정보 필요 여부
emailstring사용자의 이메일 주소아니요
email_verifiedboolean이메일 주소가 검증되었는지 여부아니요

phone

클레임 이름유형설명사용자 정보 필요 여부
phone_numberstring사용자의 전화번호아니요
phone_number_verifiedboolean전화번호가 검증되었는지 여부아니요

address

주소 클레임의 세부 사항은 OpenID Connect Core 1.0을 참조하세요.

custom_data

클레임 이름유형설명사용자 정보 필요 여부
custom_dataobject사용자의 사용자 정의 데이터

identities

클레임 이름유형설명사용자 정보 필요 여부
identitiesobject사용자의 연결된 아이덴티티
sso_identitiesarray사용자의 연결된 SSO 아이덴티티

roles

클레임 이름유형설명사용자 정보 필요 여부
rolesstring[]사용자의 역할아니요

urn:logto:scope:organizations

클레임 이름유형설명사용자 정보 필요 여부
organizationsstring[]사용자가 속한 조직 ID아니요
organization_dataobject[]사용자가 속한 조직 데이터

urn:logto:scope:organization_roles

클레임 이름유형설명사용자 정보 필요 여부
organization_rolesstring[]사용자가 속한 조직 역할, 형식은 <organization_id>:<role_name>아니요

성능과 데이터 크기를 고려할 때, "사용자 정보 필요 여부"가 "예"인 경우, 해당 클레임은 ID 토큰에 나타나지 않으며, userinfo 엔드포인트 응답에서 반환됩니다.

API 리소스 및 조직

먼저 🔐 역할 기반 접근 제어 (RBAC)를 읽어 Logto RBAC의 기본 개념과 API 리소스를 적절히 설정하는 방법을 이해하는 것을 권장합니다.

Logto 클라이언트 구성하기

API 리소스를 설정한 후, 애플리케이션에서 Logto를 구성할 때 이를 추가할 수 있습니다:

lib/main.dart
// LogtoConfig
final logtoConfig = const LogtoConfig(
endpoint: "<your-logto-endpoint>",
appId: "<your-app-id>",
// API 리소스를 추가하세요
resources: ["https://shopping.your-app.com/api", "https://store.your-app.com/api"],
);

각 API 리소스는 자체 권한 (스코프)을 가지고 있습니다.

예를 들어, https://shopping.your-app.com/api 리소스는 shopping:readshopping:write 권한을 가지고 있으며, https://store.your-app.com/api 리소스는 store:readstore:write 권한을 가지고 있습니다.

이러한 권한을 요청하려면, 애플리케이션에서 Logto를 구성할 때 추가할 수 있습니다:

lib/main.dart
// LogtoConfig
final logtoConfig = const LogtoConfig(
endpoint: "<your-logto-endpoint>",
appId: "<your-app-id>",
resources: ["https://shopping.your-app.com/api", "https://store.your-app.com/api"],
// Add your API resources' scopes
scopes: ["shopping:read", "shopping:write", "store:read", "store:write"]
);

스코프가 API 리소스와 별도로 정의된 것을 알 수 있습니다. 이는 OAuth 2.0을 위한 리소스 지표가 요청의 최종 스코프가 모든 대상 서비스의 모든 스코프의 데카르트 곱이 될 것이라고 명시하기 때문입니다.

따라서 위의 경우, Logto에서 정의된 스코프를 단순화할 수 있으며, 두 API 리소스 모두 접두사 없이 readwrite 스코프를 가질 수 있습니다. 그런 다음, Logto 구성에서:

lib/main.dart
// LogtoConfig
final logtoConfig = const LogtoConfig(
endpoint: "<your-logto-endpoint>",
appId: "<your-app-id>",
resources: ["https://shopping.your-app.com/api", "https://store.your-app.com/api"],
// 모든 리소스에 의해 공유되는 스코프
scopes: ["read", "write"]
);

모든 API 리소스에 대해 readwrite 스코프를 요청하게 됩니다.

노트:

API 리소스에 정의되지 않은 스코프를 요청해도 괜찮습니다. 예를 들어, API 리소스에 email 스코프가 없더라도 email 스코프를 요청할 수 있습니다. 사용 불가능한 스코프는 안전하게 무시됩니다.

성공적으로 로그인한 후, Logto는 사용자의 역할에 따라 API 리소스에 적절한 스코프를 발급합니다.

API 리소스에 대한 액세스 토큰 가져오기

특정 API 리소스에 대한 액세스 토큰을 가져오려면 getAccessToken 메서드를 사용할 수 있습니다:

lib/main.dart
Future<AccessToken?> getAccessToken(String resource) async {
var token = await logtoClient.getAccessToken(resource: resource);

return token;
}

이 메서드는 사용자가 관련 권한을 가지고 있을 때 API 리소스에 접근할 수 있는 JWT 액세스 토큰을 반환합니다. 현재 캐시된 액세스 토큰이 만료된 경우, 이 메서드는 자동으로 리프레시 토큰을 사용하여 새로운 액세스 토큰을 얻으려고 시도합니다.

조직에 대한 액세스 토큰 가져오기

API 리소스와 마찬가지로, 조직에 대한 액세스 토큰을 요청할 수도 있습니다. 이는 API 리소스 스코프 대신 조직 스코프를 사용하여 정의된 리소스에 접근해야 할 때 유용합니다.

조직이 처음이라면, 시작하기 위해 🏢 조직 (다중 테넌시)을 읽어보세요.

Logto 클라이언트를 구성할 때 LogtoUserScope.Organizations 스코프를 추가해야 합니다:

lib/main.dart
// LogtoConfig
final logtoConfig = const LogtoConfig(
endpoint: "<your-logto-endpoint>",
appId: "<your-app-id>",
scopes: [LogtoUserScopes.organizations.value]
);

사용자가 로그인하면, 사용자에 대한 조직 토큰을 가져올 수 있습니다:

// 사용자의 유효한 조직 ID는 ID 토큰 클레임 `organizations`에서 찾을 수 있습니다.
Future<AccessToken?> getOrganizationAccessToken(String organizationId) async {
var token = await logtoClient.getOrganizationToken(organizationId);

return token;
}

마이그레이션 가이드

이전 버전의 Logto Dart SDK, 버전 < 3.0.0에서 마이그레이션하는 경우:

  1. pubspec.yaml 파일을 업데이트하여 최신 버전의 Logto Dart SDK를 사용하세요.

    pubspec.yaml
    dependencies:
    logto_dart_sdk: ^3.0.0
  2. Android manifest 파일을 업데이트하여 기존의 flutter_web_auth 콜백 액티비티를 새로운 flutter_web_auth_2로 교체하세요.

    • FlutterWebAuth -> FlutterWebAuth2
    • flutter_web_auth -> flutter_web_auth_2
  3. signOut 메서드에 redirectUri를 전달하세요.

    redirectUri는 이제 signOut 메서드를 호출할 때 필요합니다. iOS 플랫폼에서는 이 매개변수가 필요 없지만, Android 및 Web 플랫폼에서는 로그인 세션을 정리하기 위한 추가 end_session 요청이 필요하므로 이 매개변수가 end_session 요청의 post_logout_redirect_uri 매개변수로 사용됩니다.

    await logtoClient.signOut(redirectUri);

문제 해결

Android 문제 해결

  • com.linusu.flutter_web_auth_2.CallbackActivity 액티비티를 포함하도록 AndroidManifest.xml을 업데이트해야 합니다. 예를 들어:

    android/app/src/main/AndroidManifest.xml
      <manifest>
    <application>

    <!-- com.linusu.flutter_web_auth_2.CallbackActivity 액티비티 추가 -->
    <activity
    android:name="com.linusu.flutter_web_auth_2.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="YOUR_CALLBACK_URL_SCHEME_HERE" />
    </intent-filter>
    </activity>

    </application>
    </manifest>
  • S+ (SDK 버전 31 이상)를 대상으로 하는 경우 android:exported에 명시적인 값을 제공해야 합니다. 이전 설치 지침을 따랐다면, 이것이 포함되지 않았을 수 있습니다. AndroidManifest.xml 파일의 com.linusu.flutter_web_auth.CallbackActivity 액티비티에 android:exported="true"를 추가해야 합니다.

  • 성공적인 로그인 후 브라우저가 닫히지 않는 경우:

    flutter_web_auth_2가 올바르게 작동하도록 하려면 AndroidManifest.xml 파일에서 모든 android:taskAffinity 항목을 제거해야 합니다. AndroidManifest.xml 파일의 메인 액티비티에 android:launchMode="singleTop"을 설정하세요.

    android/app/src/main/AndroidManifest.xml
    <activity
    android:name=".MainActivity"
    android:launchMode="singleTop"
    android:theme="@style/LaunchTheme"
    // ...
    />

추가 읽을거리

최종 사용자 흐름: 인증 흐름, 계정 흐름, 및 조직 흐름 커넥터 구성 API 보호