Skip to content

Commit a035d10

Browse files
authored
Merge pull request #52 from passageidentity/PSG-4176
PSG-4176
2 parents 0fc02ed + 6ecae2f commit a035d10

22 files changed

+258
-39
lines changed

.github/workflows/integration-tests.yml

+6-1
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ jobs:
116116
steps:
117117
- uses: actions/checkout@v4
118118

119+
- name: Set up Xcode 15.4
120+
uses: maxim-lobanov/setup-xcode@v1
121+
with:
122+
xcode-version: '15.4'
123+
119124
- name: Set up Flutter
120125
uses: subosito/flutter-action@v2
121126
with:
@@ -135,7 +140,7 @@ jobs:
135140
model: 'iPhone 14'
136141

137142
- name: Run iOS tests
138-
run: flutter test integration_test/*.dart -d D9B9FD9F-A0A3-422F-8D9B-35F8CC886FF3 --dart-define=MAILOSAUR_API_KEY=${{ secrets.MAILOSAUR_API_KEY }}
143+
run: flutter test integration_test/*.dart -d F36E59FA-5F31-4EEA-A81A-514EA3F505B4 --dart-define=MAILOSAUR_API_KEY=${{ secrets.MAILOSAUR_API_KEY }}
139144
working-directory: ./integrationtestapp
140145
env:
141146
MAILOSAUR_API_KEY: ${{ secrets.MAILOSAUR_API_KEY }}

android/build.gradle

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ group 'id.passage.passage_flutter'
22
version '1.0-SNAPSHOT'
33

44
buildscript {
5-
ext.kotlin_version = '1.7.10'
5+
ext.kotlin_version = '1.8.10'
66
repositories {
77
google()
88
mavenCentral()
99
}
1010

1111
dependencies {
12-
classpath 'com.android.tools.build:gradle:7.3.0'
12+
classpath 'com.android.tools.build:gradle:7.3.1'
1313
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
1414
}
1515
}
@@ -25,7 +25,7 @@ apply plugin: 'com.android.library'
2525
apply plugin: 'kotlin-android'
2626

2727
android {
28-
compileSdkVersion 31
28+
compileSdkVersion 34
2929

3030
compileOptions {
3131
sourceCompatibility JavaVersion.VERSION_1_8
@@ -48,7 +48,7 @@ android {
4848
dependencies {
4949
testImplementation 'org.jetbrains.kotlin:kotlin-test'
5050
testImplementation 'org.mockito:mockito-core:5.0.0'
51-
implementation 'id.passage.android:passage:1.7.3'
51+
implementation 'id.passage.android:passage:1.8.2'
5252
implementation 'com.google.code.gson:gson:2.9.0'
5353
}
5454

android/src/main/kotlin/id/passage/passage_flutter/PassageFlutter.kt

+51
Original file line numberDiff line numberDiff line change
@@ -431,4 +431,55 @@ internal class PassageFlutter(private val activity: Activity, appId: String? = n
431431

432432
// endregion
433433

434+
// region Hosted Auth
435+
436+
437+
fun hostedAuthStart(result: MethodChannel.Result) {
438+
CoroutineScope(Dispatchers.IO).launch {
439+
try {
440+
passage.hostedAuthStart()
441+
result.success(null)
442+
} catch (e: Exception) {
443+
val error = PassageFlutterError.START_HOSTED_AUTH_ERROR
444+
result.error(error.name, e.message, e.toString())
445+
}
446+
}
447+
}
448+
449+
450+
fun hostedAuthFinish(call: MethodCall, result: MethodChannel.Result) {
451+
val code = call.argument<String>("code")
452+
?: return invalidArgumentError(result)
453+
val state = call.argument<String>("state")
454+
?: return invalidArgumentError(result)
455+
CoroutineScope(Dispatchers.IO).launch {
456+
try {
457+
val authResultWithIdToken = passage.hostedAuthFinish(code, state)
458+
val jsonString = Gson().toJson(authResultWithIdToken.first)
459+
val map = mapOf(
460+
"authResult" to jsonString,
461+
"idToken" to authResultWithIdToken.second
462+
)
463+
result.success(map)
464+
} catch (e: Exception) {
465+
val error = PassageFlutterError.FINISH_HOSTED_AUTH_ERROR
466+
result.error(error.name, e.message, e.toString())
467+
}
468+
}
469+
}
470+
471+
fun hostedLogout(result: MethodChannel.Result) {
472+
CoroutineScope(Dispatchers.IO).launch {
473+
try {
474+
passage.hostedLogout()
475+
result.success(null)
476+
} catch (e: Exception) {
477+
val error = PassageFlutterError.LOGOUT_HOSTED_AUTH_ERROR
478+
result.error(error.name, e.message, e.toString())
479+
}
480+
}
481+
}
482+
483+
// endregion
484+
434485
}

android/src/main/kotlin/id/passage/passage_flutter/PassageFlutterError.kt

+3
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,7 @@ internal enum class PassageFlutterError {
1414
IDENTIFIER_EXISTS_ERROR,
1515
OTP_ACTIVATION_EXCEEDED_ATTEMPTS,
1616
SOCIAL_AUTH_ERROR,
17+
START_HOSTED_AUTH_ERROR,
18+
LOGOUT_HOSTED_AUTH_ERROR,
19+
FINISH_HOSTED_AUTH_ERROR
1720
}

android/src/main/kotlin/id/passage/passage_flutter/PassageFlutterPlugin.kt

+3
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ class PassageFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
7171
"editPasskeyName" -> passageFlutter?.editPasskeyName(call, result)
7272
"changeEmail" -> passageFlutter?.changeEmail(call, result)
7373
"changePhone" -> passageFlutter?.changePhone(call, result)
74+
"hostedAuthStart" -> passageFlutter?.hostedAuthStart(result)
75+
"hostedAuthFinish" -> passageFlutter?.hostedAuthFinish(call, result)
76+
"hostedLogout" -> passageFlutter?.hostedLogout(result)
7477
else -> {
7578
result.notImplemented()
7679
}

integrationtestapp/android/build.gradle

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
buildscript {
2-
ext.kotlin_version = '1.7.10'
2+
ext.kotlin_version = '1.8.10'
33
repositories {
44
google()
55
mavenCentral()
66
}
77

88
dependencies {
9-
classpath 'com.android.tools.build:gradle:7.3.0'
9+
classpath 'com.android.tools.build:gradle:7.3.1'
1010
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
1111
}
1212
}

integrationtestapp/android/settings.gradle

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ pluginManagement {
1818

1919
plugins {
2020
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
21-
id "com.android.application" version "7.3.0" apply false
22-
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
21+
id "com.android.application" version "7.3.1" apply false
22+
id "org.jetbrains.kotlin.android" version "1.8.10" apply false
2323
}
2424

2525
include ":app"

integrationtestapp/integration_test/change_user_info_test.dart

-3
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ void main() {
1414
setUp(() async {
1515
if (!kIsWeb) {
1616
String basePath = IntegrationTestConfig.apiBaseUrl;
17-
if (PlatformHelper.isAndroid) {
18-
basePath += '/v1';
19-
}
2017
await passage.overrideBasePath(basePath);
2118
}
2219
});

integrationtestapp/integration_test/current_user_test.dart

-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ void main() {
1313
setUp(() async {
1414
if (!kIsWeb) {
1515
String basePath = IntegrationTestConfig.apiBaseUrl;
16-
if (PlatformHelper.isAndroid) {
17-
basePath += '/v1';
18-
}
1916
await passage.overrideBasePath(basePath);
2017
}
2118
});

integrationtestapp/integration_test/helper/integration_test_config.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import 'package:passage_flutter/passage_flutter_models/passage_user.dart';
22

33
class IntegrationTestConfig {
4-
static const String apiBaseUrl = "https://auth-uat.passage.dev";
4+
static const String apiBaseUrl = "https://auth-uat.passage.dev/v1";
55
static const String appIdOtp = "Ezbk6fSdx9pNQ7v7UbVEnzeC";
66
static const String appIdMagicLink = "Pea2GdtBHN3esylK4ZRlF19U";
77
static const int waitTimeMilliseconds = 8000;

integrationtestapp/integration_test/magic_link_test.dart

-3
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ void main() {
1414
setUpAll(() async {
1515
if (!kIsWeb) {
1616
String basePath = IntegrationTestConfig.apiBaseUrl;
17-
if (PlatformHelper.isAndroid) {
18-
basePath += '/v1';
19-
}
2017
await passage.overrideBasePath(basePath);
2118
}
2219
});

integrationtestapp/integration_test/otp_test.dart

-3
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ void main() {
1414
setUp(() async {
1515
if (!kIsWeb) {
1616
String basePath = IntegrationTestConfig.apiBaseUrl;
17-
if (PlatformHelper.isAndroid) {
18-
basePath += '/v1';
19-
}
2017
await passage.overrideBasePath(basePath);
2118
}
2219
});

integrationtestapp/integration_test/token_store_test.dart

-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ void main() {
1313
setUp(() async {
1414
if (!kIsWeb) {
1515
String basePath = IntegrationTestConfig.apiBaseUrl;
16-
if (PlatformHelper.isAndroid) {
17-
basePath += '/v1';
18-
}
1916
await passage.overrideBasePath(basePath);
2017
}
2118
});

integrationtestapp/ios/Podfile.lock

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
PODS:
2+
- AnyCodable-FlightSchool (0.6.7)
23
- Flutter (1.0.0)
34
- integration_test (0.0.1):
45
- Flutter
5-
- Passage (1.6.0):
6+
- Passage (1.8.1):
7+
- AnyCodable-FlightSchool
68
- SwiftKeychainWrapper
79
- passage_flutter (0.7.3):
810
- Flutter
9-
- Passage (= 1.6.0)
11+
- Passage (= 1.8.1)
1012
- SwiftKeychainWrapper (4.0.1)
1113

1214
DEPENDENCIES:
@@ -16,6 +18,7 @@ DEPENDENCIES:
1618

1719
SPEC REPOS:
1820
trunk:
21+
- AnyCodable-FlightSchool
1922
- Passage
2023
- SwiftKeychainWrapper
2124

@@ -28,10 +31,11 @@ EXTERNAL SOURCES:
2831
:path: ".symlinks/plugins/passage_flutter/ios"
2932

3033
SPEC CHECKSUMS:
34+
AnyCodable-FlightSchool: 261cbe76757802b17d471b9059b21e6fa5edf57b
3135
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
3236
integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4
33-
Passage: 347004a76cb6677646c1f46ea48766cab5e9eb8a
34-
passage_flutter: 484b9c4b625e0d17c6907cf4e9e57a1d5a1df50d
37+
Passage: d6a41db58c3256e85a73ba993e515e7d1995b04f
38+
passage_flutter: 8539d3e5e33da9b00454de4e371f9d3371a4de00
3539
SwiftKeychainWrapper: 807ba1d63c33a7d0613288512399cd1eda1e470c
3640

3741
PODFILE CHECKSUM: 6a26cacf19c7fcde87d9c59e8f24986138e82f3b

ios/Classes/PassageFlutter.swift

+36-8
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ internal class PassageFlutter {
3535
}
3636
let authResult = try await passage.registerWithPasskey(identifier: identifier, options: passkeyCreationOptions)
3737
result(convertToJsonString(codable: authResult))
38-
} catch PassageASAuthorizationError.canceled {
38+
} catch RegisterWithPasskeyError.canceled {
3939
let error = PassageFlutterError.USER_CANCELLED.defaultFlutterError
4040
result(error)
4141
} catch {
@@ -60,7 +60,7 @@ internal class PassageFlutter {
6060
do {
6161
let authResult = try await passage.loginWithPasskey(identifier: identifier)
6262
result(convertToJsonString(codable: authResult))
63-
} catch PassageASAuthorizationError.canceled {
63+
} catch LoginWithPasskeyError.canceled {
6464
let error = PassageFlutterError.USER_CANCELLED.defaultFlutterError
6565
result(error)
6666
} catch {
@@ -138,7 +138,7 @@ internal class PassageFlutter {
138138
result(convertToJsonString(codable: authResult))
139139
} catch {
140140
var errorCode = PassageFlutterError.OTP_ERROR.rawValue
141-
if case PassageOTPError.exceededAttempts = error {
141+
if case OneTimePasscodeActivateError.exceededAttempts = error {
142142
errorCode = PassageFlutterError.OTP_ACTIVATION_EXCEEDED_ATTEMPTS.rawValue
143143
}
144144
let error = FlutterError(
@@ -339,11 +339,7 @@ internal class PassageFlutter {
339339
internal func getAppInfo(result: @escaping FlutterResult) {
340340
Task {
341341
do {
342-
guard let appInfo = try await PassageAuth.appInfo() else {
343-
let error = PassageFlutterError.APP_INFO_ERROR.defaultFlutterError
344-
result(error)
345-
return
346-
}
342+
let appInfo = try await passage.appInfo()
347343
result(convertToJsonString(codable: appInfo))
348344
} catch {
349345
let error = FlutterError(
@@ -526,6 +522,38 @@ internal class PassageFlutter {
526522
result(convertToJsonString(codable: user))
527523
}
528524
}
525+
526+
internal func hostedAuth(result: @escaping FlutterResult) {
527+
Task {
528+
do {
529+
let authResult = try await passage.hostedAuth()
530+
result(convertToJsonString(codable: authResult))
531+
} catch {
532+
let error = FlutterError(
533+
code: PassageFlutterError.START_HOSTED_AUTH_ERROR.rawValue,
534+
message: error.localizedDescription,
535+
details: nil
536+
)
537+
result(error)
538+
}
539+
}
540+
}
541+
542+
internal func hostedLogout(result: @escaping FlutterResult) {
543+
Task {
544+
do {
545+
try await passage.hostedLogout()
546+
result(nil)
547+
} catch {
548+
let error = FlutterError(
549+
code: PassageFlutterError.LOGOUT_HOSTED_AUTH_ERROR.rawValue,
550+
message: error.localizedDescription,
551+
details: nil
552+
)
553+
result(error)
554+
}
555+
}
556+
}
529557

530558
}
531559

ios/Classes/PassageFlutterError.swift

+6
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,19 @@ internal enum PassageFlutterError: String {
1515
case CHANGE_PHONE_ERROR
1616
case IDENTIFIER_EXISTS_ERROR
1717
case OTP_ACTIVATION_EXCEEDED_ATTEMPTS
18+
case START_HOSTED_AUTH_ERROR
19+
case FINISH_HOSTED_AUTH_ERROR
20+
case LOGOUT_HOSTED_AUTH_ERROR
1821

1922
var defaultMessage: String {
2023
switch self {
2124
case .INVALID_ARGUMENT: return "Invalid or missing argument"
2225
case .PASSKEYS_NOT_SUPPORTED: return "Passkeys only supported in iOS 16 and newer"
2326
case .USER_CANCELLED: return "User cancelled"
2427
case .APP_INFO_ERROR: return "Error getting app info"
28+
case .START_HOSTED_AUTH_ERROR: return "Error starting hosted authentication"
29+
case .FINISH_HOSTED_AUTH_ERROR: return "Error finishing hosted authentication"
30+
case .LOGOUT_HOSTED_AUTH_ERROR: return "Error logging out from hosted authentication"
2531
default: return ""
2632
}
2733
}

ios/Classes/PassageFlutterPlugin.swift

+4
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ public class PassageFlutterPlugin: NSObject, FlutterPlugin {
7676
passageFlutter.changePhone(arguments: call.arguments, result: result)
7777
case "identifierExists":
7878
passageFlutter.identifierExists(arguments: call.arguments, result: result)
79+
case "hostedAuth":
80+
passageFlutter.hostedAuth(result: result)
81+
case "hostedLogout":
82+
passageFlutter.hostedLogout(result: result)
7983
default:
8084
result(FlutterMethodNotImplemented)
8185
}

ios/passage_flutter.podspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Pod::Spec.new do |s|
1111
s.source = { :path => '.' }
1212
s.source_files = 'Classes/**/*'
1313
s.dependency 'Flutter'
14-
s.dependency 'Passage', '1.6.0'
14+
s.dependency 'Passage', '1.8.1'
1515
s.platform = :ios, '14.0'
1616

1717
# Flutter.framework does not contain a i386 slice.

0 commit comments

Comments
 (0)