Skip to content

Commit 3c91508

Browse files
authored
Add MFA Support to GoTrue (#124)
* Add interface for MFA methods * Implement all MFA methods * Move EventEmitter * Code cleanup * Do not use enums on some response models * Adding MFA Flow to Example project * Add MFA Enrollment view * Use Dependency Container * Add MFA verify flow * Fix tests * Remove commented code
1 parent d102867 commit 3c91508

25 files changed

+1134
-324
lines changed

.swiftpm/xcode/xcshareddata/xcschemes/Supabase-Package.xcscheme

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,62 @@
2020
ReferencedContainer = "container:">
2121
</BuildableReference>
2222
</BuildActionEntry>
23+
<BuildActionEntry
24+
buildForTesting = "YES"
25+
buildForRunning = "YES"
26+
buildForProfiling = "YES"
27+
buildForArchiving = "YES"
28+
buildForAnalyzing = "YES">
29+
<BuildableReference
30+
BuildableIdentifier = "primary"
31+
BlueprintIdentifier = "GoTrue"
32+
BuildableName = "GoTrue"
33+
BlueprintName = "GoTrue"
34+
ReferencedContainer = "container:">
35+
</BuildableReference>
36+
</BuildActionEntry>
37+
<BuildActionEntry
38+
buildForTesting = "YES"
39+
buildForRunning = "YES"
40+
buildForProfiling = "YES"
41+
buildForArchiving = "YES"
42+
buildForAnalyzing = "YES">
43+
<BuildableReference
44+
BuildableIdentifier = "primary"
45+
BlueprintIdentifier = "PostgREST"
46+
BuildableName = "PostgREST"
47+
BlueprintName = "PostgREST"
48+
ReferencedContainer = "container:">
49+
</BuildableReference>
50+
</BuildActionEntry>
51+
<BuildActionEntry
52+
buildForTesting = "YES"
53+
buildForRunning = "YES"
54+
buildForProfiling = "YES"
55+
buildForArchiving = "YES"
56+
buildForAnalyzing = "YES">
57+
<BuildableReference
58+
BuildableIdentifier = "primary"
59+
BlueprintIdentifier = "Realtime"
60+
BuildableName = "Realtime"
61+
BlueprintName = "Realtime"
62+
ReferencedContainer = "container:">
63+
</BuildableReference>
64+
</BuildActionEntry>
65+
<BuildActionEntry
66+
buildForTesting = "YES"
67+
buildForRunning = "YES"
68+
buildForProfiling = "YES"
69+
buildForArchiving = "YES"
70+
buildForAnalyzing = "YES">
71+
<BuildableReference
72+
BuildableIdentifier = "primary"
73+
BlueprintIdentifier = "Storage"
74+
BuildableName = "Storage"
75+
BlueprintName = "Storage"
76+
ReferencedContainer = "container:">
77+
</BuildableReference>
78+
</BuildActionEntry>
2379
</BuildActionEntries>
2480
</BuildAction>
2581
<TestAction

Examples/Examples.xcodeproj/project.pbxproj

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
7956406A2955AFBD0088A06F /* ErrorText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 795640692955AFBD0088A06F /* ErrorText.swift */; };
3030
7956406D2955B3500088A06F /* SwiftUINavigation in Frameworks */ = {isa = PBXBuildFile; productRef = 7956406C2955B3500088A06F /* SwiftUINavigation */; };
3131
795640702955B5190088A06F /* IdentifiedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = 7956406F2955B5190088A06F /* IdentifiedCollections */; };
32+
796298992AEBBA77000AA957 /* MFAFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796298982AEBBA77000AA957 /* MFAFlow.swift */; };
33+
7962989D2AEBC6F9000AA957 /* SVGView in Frameworks */ = {isa = PBXBuildFile; productRef = 7962989C2AEBC6F9000AA957 /* SVGView */; };
3234
79719ECE2ADF26C400737804 /* Supabase in Frameworks */ = {isa = PBXBuildFile; productRef = 79719ECD2ADF26C400737804 /* Supabase */; };
3335
79C591DC2AE0880F0088A9C8 /* ProductSampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C591DB2AE0880F0088A9C8 /* ProductSampleApp.swift */; };
3436
79C591DE2AE0880F0088A9C8 /* AppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C591DD2AE0880F0088A9C8 /* AppView.swift */; };
@@ -78,6 +80,8 @@
7880
795640652955AE9C0088A06F /* TodoListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodoListView.swift; sourceTree = "<group>"; };
7981
795640672955AEB30088A06F /* Models.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Models.swift; sourceTree = "<group>"; };
8082
795640692955AFBD0088A06F /* ErrorText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorText.swift; sourceTree = "<group>"; };
83+
796298982AEBBA77000AA957 /* MFAFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MFAFlow.swift; sourceTree = "<group>"; };
84+
7962989A2AEBBD9F000AA957 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
8185
79C591D92AE0880F0088A9C8 /* ProductSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ProductSample.app; sourceTree = BUILT_PRODUCTS_DIR; };
8286
79C591DB2AE0880F0088A9C8 /* ProductSampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductSampleApp.swift; sourceTree = "<group>"; };
8387
79C591DD2AE0880F0088A9C8 /* AppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppView.swift; sourceTree = "<group>"; };
@@ -109,6 +113,7 @@
109113
files = (
110114
795640702955B5190088A06F /* IdentifiedCollections in Frameworks */,
111115
7956406D2955B3500088A06F /* SwiftUINavigation in Frameworks */,
116+
7962989D2AEBC6F9000AA957 /* SVGView in Frameworks */,
112117
79719ECE2ADF26C400737804 /* Supabase in Frameworks */,
113118
);
114119
runOnlyForDeploymentPostprocessing = 0;
@@ -231,6 +236,7 @@
231236
793895C82954ABFF0044F2B8 /* Examples */ = {
232237
isa = PBXGroup;
233238
children = (
239+
7962989A2AEBBD9F000AA957 /* Info.plist */,
234240
793895CD2954AC000044F2B8 /* Assets.xcassets */,
235241
7956405F2954AE140088A06F /* AuthView.swift */,
236242
793895CF2954AC000044F2B8 /* Examples.entitlements */,
@@ -244,6 +250,7 @@
244250
795640692955AFBD0088A06F /* ErrorText.swift */,
245251
794EF1212955F26A008C9526 /* AddTodoListView.swift */,
246252
794EF1232955F3DE008C9526 /* TodoListRow.swift */,
253+
796298982AEBBA77000AA957 /* MFAFlow.swift */,
247254
);
248255
path = Examples;
249256
sourceTree = "<group>";
@@ -339,6 +346,7 @@
339346
7956406C2955B3500088A06F /* SwiftUINavigation */,
340347
7956406F2955B5190088A06F /* IdentifiedCollections */,
341348
79719ECD2ADF26C400737804 /* Supabase */,
349+
7962989C2AEBC6F9000AA957 /* SVGView */,
342350
);
343351
productName = Examples;
344352
productReference = 793895C62954ABFF0044F2B8 /* Examples.app */;
@@ -394,6 +402,7 @@
394402
packageReferences = (
395403
7956406B2955B3500088A06F /* XCRemoteSwiftPackageReference "swiftui-navigation" */,
396404
7956406E2955B5190088A06F /* XCRemoteSwiftPackageReference "swift-identified-collections" */,
405+
7962989B2AEBC6F9000AA957 /* XCRemoteSwiftPackageReference "SVGView" */,
397406
);
398407
productRefGroup = 793895C72954ABFF0044F2B8 /* Products */;
399408
projectDirPath = "";
@@ -432,6 +441,7 @@
432441
isa = PBXSourcesBuildPhase;
433442
buildActionMask = 2147483647;
434443
files = (
444+
796298992AEBBA77000AA957 /* MFAFlow.swift in Sources */,
435445
793895CC2954ABFF0044F2B8 /* RootView.swift in Sources */,
436446
7956406A2955AFBD0088A06F /* ErrorText.swift in Sources */,
437447
794EF1242955F3DE008C9526 /* TodoListRow.swift in Sources */,
@@ -534,6 +544,7 @@
534544
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
535545
GCC_WARN_UNUSED_FUNCTION = YES;
536546
GCC_WARN_UNUSED_VARIABLE = YES;
547+
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
537548
LD_RUNPATH_SEARCH_PATHS = (
538549
"$(inherited)",
539550
"@executable_path/Frameworks",
@@ -597,6 +608,7 @@
597608
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
598609
GCC_WARN_UNUSED_FUNCTION = YES;
599610
GCC_WARN_UNUSED_VARIABLE = YES;
611+
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
600612
LD_RUNPATH_SEARCH_PATHS = (
601613
"$(inherited)",
602614
"@executable_path/Frameworks",
@@ -624,6 +636,8 @@
624636
ENABLE_HARDENED_RUNTIME = YES;
625637
ENABLE_PREVIEWS = YES;
626638
GENERATE_INFOPLIST_FILE = YES;
639+
INFOPLIST_FILE = Examples/Info.plist;
640+
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
627641
PRODUCT_BUNDLE_IDENTIFIER = com.supabase.Examples;
628642
PRODUCT_NAME = "$(TARGET_NAME)";
629643
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -642,6 +656,8 @@
642656
ENABLE_HARDENED_RUNTIME = YES;
643657
ENABLE_PREVIEWS = YES;
644658
GENERATE_INFOPLIST_FILE = YES;
659+
INFOPLIST_FILE = Examples/Info.plist;
660+
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
645661
PRODUCT_BUNDLE_IDENTIFIER = com.supabase.Examples;
646662
PRODUCT_NAME = "$(TARGET_NAME)";
647663
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -743,6 +759,14 @@
743759
minimumVersion = 1.0.0;
744760
};
745761
};
762+
7962989B2AEBC6F9000AA957 /* XCRemoteSwiftPackageReference "SVGView" */ = {
763+
isa = XCRemoteSwiftPackageReference;
764+
repositoryURL = "https://github.com/exyte/SVGView";
765+
requirement = {
766+
kind = upToNextMajorVersion;
767+
minimumVersion = 1.0.6;
768+
};
769+
};
746770
/* End XCRemoteSwiftPackageReference section */
747771

748772
/* Begin XCSwiftPackageProductDependency section */
@@ -756,6 +780,11 @@
756780
package = 7956406E2955B5190088A06F /* XCRemoteSwiftPackageReference "swift-identified-collections" */;
757781
productName = IdentifiedCollections;
758782
};
783+
7962989C2AEBC6F9000AA957 /* SVGView */ = {
784+
isa = XCSwiftPackageProductDependency;
785+
package = 7962989B2AEBC6F9000AA957 /* XCRemoteSwiftPackageReference "SVGView" */;
786+
productName = SVGView;
787+
};
759788
79719ECD2ADF26C400737804 /* Supabase */ = {
760789
isa = XCSwiftPackageProductDependency;
761790
productName = Supabase;

Examples/Examples/AuthView.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import GoTrue
99
import SwiftUI
1010

11+
@MainActor
1112
final class AuthController: ObservableObject {
1213
@Published var session: Session?
1314

@@ -18,6 +19,16 @@ final class AuthController: ObservableObject {
1819

1920
return id
2021
}
22+
23+
func observeAuth() async {
24+
for await event in await supabase.auth.onAuthStateChange() {
25+
guard event == .signedIn || event == .signedOut else {
26+
return
27+
}
28+
29+
session = try? await supabase.auth.session
30+
}
31+
}
2132
}
2233

2334
struct AuthView: View {
@@ -74,7 +85,8 @@ struct AuthView: View {
7485
case .signIn:
7586
try await supabase.auth.signIn(email: email, password: password)
7687
case .signUp:
77-
try await supabase.auth.signUp(email: email, password: password)
88+
try await supabase.auth.signUp(
89+
email: email, password: password, redirectTo: URL(string: "com.supabase.Examples://")!)
7890
}
7991
} catch {
8092
withAnimation {

Examples/Examples/HomeView.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
import SwiftUI
99

1010
struct HomeView: View {
11+
@EnvironmentObject var auth: AuthController
12+
13+
@State private var mfaStatus: MFAStatus?
14+
1115
var body: some View {
1216
NavigationStack {
1317
TodoListView()
@@ -21,6 +25,32 @@ struct HomeView: View {
2125
}
2226
}
2327
}
28+
.task {
29+
mfaStatus = await verifyMFAStatus()
30+
}
31+
.sheet(unwrapping: $mfaStatus) { $mfaStatus in
32+
MFAFlow(status: mfaStatus)
33+
}
34+
}
35+
36+
private func verifyMFAStatus() async -> MFAStatus? {
37+
do {
38+
let aal = try await supabase.auth.mfa.getAuthenticatorAssuranceLevel()
39+
switch (aal.currentLevel, aal.nextLevel) {
40+
case ("aal1", "aal1"):
41+
return .unenrolled
42+
case ("aal1", "aal2"):
43+
return .unverified
44+
case ("aal2", "aal2"):
45+
return .verified
46+
case ("aal2", "aal1"):
47+
return .disabled
48+
default:
49+
return nil
50+
}
51+
} catch {
52+
return nil
53+
}
2454
}
2555
}
2656

Examples/Examples/Info.plist

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleURLTypes</key>
6+
<array>
7+
<dict>
8+
<key>CFBundleTypeRole</key>
9+
<string>Editor</string>
10+
<key>CFBundleURLSchemes</key>
11+
<array>
12+
<string>com.supabase.Examples://</string>
13+
</array>
14+
</dict>
15+
</array>
16+
</dict>
17+
</plist>

0 commit comments

Comments
 (0)