From c499e01240f3efcd848c7f21f049a764f4550cf7 Mon Sep 17 00:00:00 2001 From: "george.bafaloukas" Date: Thu, 7 May 2026 15:09:33 +0100 Subject: [PATCH 1/2] Adding updated Passkeys sample branded in Ping Identity colors --- .../.github/copilot-instructions.md | 14 + .../PasskeysSwiftUI/.gitignore | 62 ++ .../PasskeysSwiftUI/Config/Debug.xcconfig | 8 + .../Config/PasskeysSwiftUI.entitlements | 11 + .../PasskeysSwiftUI/Config/Release.xcconfig | 8 + .../PasskeysSwiftUI/Config/Shared.xcconfig | 39 ++ .../PasskeysSwiftUI/Config/Tests.xcconfig | 14 + .../PasskeysSwiftUI.xcodeproj/project.pbxproj | 538 ++++++++++++++++++ .../xcshareddata/swiftpm/Package.resolved | 114 ++++ .../xcschemes/PasskeysSwiftUI.xcscheme | 98 ++++ .../PasskeysSwiftUI/App/PasskeysApp.swift | 20 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 35 ++ .../Assets.xcassets/Contents.json | 6 + .../Logo.imageset/Contents.json | 21 + .../Logo.imageset/Ping Identity Logo.png | Bin 0 -> 23323 bytes .../Config/AppConfiguration.swift | 57 ++ .../PasskeysSwiftUI.xctestplan | 35 ++ .../ViewModels/AuthenticatedViewModel.swift | 45 ++ .../ViewModels/LoginViewModel.swift | 44 ++ .../ViewModels/RegistrationViewModel.swift | 46 ++ .../Views/AuthenticatedView.swift | 182 ++++++ .../FidoAuthenticationCallbackView.swift | 75 +++ .../FidoRegistrationCallbackView.swift | 79 +++ .../Views/Callbacks/NameCallbackView.swift | 26 + .../Callbacks/PasswordCallbackView.swift | 25 + .../Views/Components/Theme.swift | 189 ++++++ .../PasskeysSwiftUI/Views/ContentView.swift | 84 +++ .../Views/ContinueNodeView.swift | 61 ++ .../PasskeysSwiftUI/Views/LoginView.swift | 64 +++ .../PasskeysSwiftUI/Views/SettingsView.swift | 106 ++++ .../PasskeysSwiftUIUITests.swift | 26 + .../PasskeysSwiftUI/README.md | 82 +++ 33 files changed, 2225 insertions(+) create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/.github/copilot-instructions.md create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/.gitignore create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/Debug.xcconfig create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/PasskeysSwiftUI.entitlements create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/Release.xcconfig create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/Shared.xcconfig create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/Tests.xcconfig create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI.xcodeproj/project.pbxproj create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI.xcodeproj/xcshareddata/xcschemes/PasskeysSwiftUI.xcscheme create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/App/PasskeysApp.swift create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Assets.xcassets/Contents.json create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Assets.xcassets/Logo.imageset/Contents.json create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Assets.xcassets/Logo.imageset/Ping Identity Logo.png create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Config/AppConfiguration.swift create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/PasskeysSwiftUI.xctestplan create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/ViewModels/AuthenticatedViewModel.swift create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/ViewModels/LoginViewModel.swift create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/ViewModels/RegistrationViewModel.swift create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/AuthenticatedView.swift create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/Callbacks/FidoAuthenticationCallbackView.swift create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/Callbacks/FidoRegistrationCallbackView.swift create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/Callbacks/NameCallbackView.swift create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/Callbacks/PasswordCallbackView.swift create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/Components/Theme.swift create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/ContentView.swift create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/ContinueNodeView.swift create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/LoginView.swift create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/SettingsView.swift create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUIUITests/PasskeysSwiftUIUITests.swift create mode 100644 iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/README.md diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/.github/copilot-instructions.md b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/.github/copilot-instructions.md new file mode 100644 index 00000000..8e82ed24 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/.github/copilot-instructions.md @@ -0,0 +1,14 @@ +# Copilot Custom Instructions + +- This repository uses Swift 6.1+ and SwiftUI for iOS 18+ apps. All code should follow modern Swift and SwiftUI best practices. +- This is an iOS project NOT a pure Swift Package or macOS project. It utlises a local Swift Package which is wrapped in an Xcode project. This makes it easier for agents to work on the project. +- Use the Model-View (MV) pattern with native SwiftUI state management (`@State`, `@Observable`, `@Environment`, `@Binding`). Do not use ViewModels or MVVM. +- All concurrency must use Swift Concurrency (async/await, actors, @MainActor). Do not use GCD or completion handlers. +- Write all new code and features inside the Swift Package (`YourAppPackage`), not in the app shell. +- Use the Swift Testing framework (`@Test`, `#expect`, `#require`) for all tests. Place tests in the package's `Tests/` directory. +- When running tests use the `test_sim_name_ws` tool do not use `swift_package_test`.- +- Use XcodeBuildMCP tools for building, testing, and automation. Prefer these over raw xcodebuild or CLI commands. +- For data persistence, use SwiftData (never CoreData), though only use for complex scenarios, prefer simpler options first e.g. UserDefaults. +- Always provide accessibility labels and identifiers for UI elements. +- Never log sensitive information or use insecure network calls. +- For full style, architecture, and workflow details, refer to the project documentation in [`template/.cursor/rules/`](../.cursor/rules/). diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/.gitignore b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/.gitignore new file mode 100644 index 00000000..fda4de3a --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/.gitignore @@ -0,0 +1,62 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## Obj-C/Swift specific +*.hmap + +## App packaging +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +# Package.resolved +# *.xcodeproj +# +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +# .swiftpm + +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# Pods/ +# +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build/ + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output \ No newline at end of file diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/Debug.xcconfig b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/Debug.xcconfig new file mode 100644 index 00000000..75b2eb20 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/Debug.xcconfig @@ -0,0 +1,8 @@ +// Debug.xcconfig +// Debug configuration for iOS projects - minimal overrides only +// Generated by XcodeBuildMCP + +#include "Shared.xcconfig" + +// No additional debug-specific overrides needed +// All debug settings use Xcode project defaults \ No newline at end of file diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/PasskeysSwiftUI.entitlements b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/PasskeysSwiftUI.entitlements new file mode 100644 index 00000000..96bececb --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/PasskeysSwiftUI.entitlements @@ -0,0 +1,11 @@ + + + + + com.apple.developer.associated-domains + + webcredentials:openam-bafaloukas.forgeblocks.com?mode=develop + webcredentials:openam-bafaloukas.forgeblocks.com + + + diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/Release.xcconfig b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/Release.xcconfig new file mode 100644 index 00000000..67bf1f04 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/Release.xcconfig @@ -0,0 +1,8 @@ +// Release.xcconfig +// Release configuration for iOS projects - minimal overrides only +// Generated by XcodeBuildMCP + +#include "Shared.xcconfig" + +// No additional release-specific overrides needed +// All release settings use Xcode project defaults \ No newline at end of file diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/Shared.xcconfig b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/Shared.xcconfig new file mode 100644 index 00000000..e41ee57a --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/Shared.xcconfig @@ -0,0 +1,39 @@ +// Shared.xcconfig +// Minimal shared configuration for scaffold tool customization +// All other settings use Xcode project defaults +// Generated by XcodeBuildMCP + +// ========================================== +// Project Identity +// ========================================== +PRODUCT_NAME = PasskeysSwiftUI +PRODUCT_DISPLAY_NAME = PasskeysSwiftUI +PRODUCT_BUNDLE_IDENTIFIER = com.pingidentity.passkeysswiftui +MARKETING_VERSION = 1.0.0 +CURRENT_PROJECT_VERSION = 1 + +// ========================================== +// Platform Configuration +// ========================================== +IPHONEOS_DEPLOYMENT_TARGET = 18.0 + +// (1 == iPhone, 2 == iPad) +TARGETED_DEVICE_FAMILY = 1,2 + +// ========================================== +// Info PLIST +// ========================================== +GENERATE_INFOPLIST_FILE = YES +INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationLandscapeRight UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationPortrait +INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = UIInterfaceOrientationLandscapeRight UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown + +// ========================================== +// Info PLIST Keys +// ========================================== +INFOPLIST_KEY_NSFaceIDUsageDescription = This app uses Face ID for biometric authentication with passkeys. + +// ========================================== +// Entitlements +// ========================================== +// AI agents can modify Config/PasskeysSwiftUI.entitlements to add capabilities +CODE_SIGN_ENTITLEMENTS = Config/PasskeysSwiftUI.entitlements diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/Tests.xcconfig b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/Tests.xcconfig new file mode 100644 index 00000000..49318f6b --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/Tests.xcconfig @@ -0,0 +1,14 @@ +// Tests.xcconfig +// Test configuration for iOS projects - minimal overrides only +// Generated by XcodeBuildMCP + +#include "Shared.xcconfig" + +// ========================================== +// Test Target Settings (Customizable by scaffold tool) +// ========================================== +PRODUCT_BUNDLE_IDENTIFIER = com.pingidentity.passkeysswiftui +TEST_TARGET_NAME = PasskeysSwiftUI + +// Fix duplicate module name issue +PRODUCT_MODULE_NAME = $(PRODUCT_NAME)UITests \ No newline at end of file diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI.xcodeproj/project.pbxproj b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI.xcodeproj/project.pbxproj new file mode 100644 index 00000000..8f15acbd --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI.xcodeproj/project.pbxproj @@ -0,0 +1,538 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 43D0BE5DEADE4B9F1BB3EB28 /* PingJourney in Frameworks */ = {isa = PBXBuildFile; productRef = 81158D7A074F1B72DD4C07DE /* PingJourney */; }; + 651E879EEB931228CEF2D31A /* PingOidc in Frameworks */ = {isa = PBXBuildFile; productRef = 06B04414648276D65C9BDF68 /* PingOidc */; }; + 93D1A04D884C046026324BFC /* PingOrchestrate in Frameworks */ = {isa = PBXBuildFile; productRef = 4A04B7FD6BA345628F49F01E /* PingOrchestrate */; }; + 9CF36A7813727C9D8D162716 /* PingJourneyPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = 62EB33A1CFDCF3A274C93942 /* PingJourneyPlugin */; }; + EC9C5CCB67FD2FC3CA415125 /* PingLogger in Frameworks */ = {isa = PBXBuildFile; productRef = E256473691CDBA1288A61EF4 /* PingLogger */; }; + EE1BA5E17E12F97795B4A26B /* PingFido in Frameworks */ = {isa = PBXBuildFile; productRef = 9076D702DE7ED6A5139E4106 /* PingFido */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 8B41F65D2DEDD0D6001A66F9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8B41F63D2DEDD0D5001A66F9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8B41F6442DEDD0D5001A66F9; + remoteInfo = PasskeysSwiftUI; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 8B41F6452DEDD0D5001A66F9 /* PasskeysSwiftUI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PasskeysSwiftUI.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 8B41F65C2DEDD0D6001A66F9 /* PasskeysSwiftUI.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PasskeysSwiftUI.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 8BD71C0A2DEE41E000CEDD92 /* Exceptions for "Config" folder in "PasskeysSwiftUI" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Debug.xcconfig, + Release.xcconfig, + Shared.xcconfig, + Tests.xcconfig, + ); + target = 8B41F6442DEDD0D5001A66F9 /* PasskeysSwiftUI */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 8B41F6472DEDD0D5001A66F9 /* PasskeysSwiftUI */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = PasskeysSwiftUI; + sourceTree = ""; + }; + 8B41F65F2DEDD0D6001A66F9 /* PasskeysSwiftUIUITests */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = PasskeysSwiftUIUITests; + sourceTree = ""; + }; + 8BD71C052DEE41D800CEDD92 /* Config */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 8BD71C0A2DEE41E000CEDD92 /* Exceptions for "Config" folder in "PasskeysSwiftUI" target */, + ); + path = Config; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8B41F6422DEDD0D5001A66F9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 43D0BE5DEADE4B9F1BB3EB28 /* PingJourney in Frameworks */, + EE1BA5E17E12F97795B4A26B /* PingFido in Frameworks */, + 651E879EEB931228CEF2D31A /* PingOidc in Frameworks */, + 93D1A04D884C046026324BFC /* PingOrchestrate in Frameworks */, + EC9C5CCB67FD2FC3CA415125 /* PingLogger in Frameworks */, + 9CF36A7813727C9D8D162716 /* PingJourneyPlugin in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8B41F6592DEDD0D6001A66F9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 8B41F63C2DEDD0D5001A66F9 = { + isa = PBXGroup; + children = ( + 8BD71C052DEE41D800CEDD92 /* Config */, + 8B41F6472DEDD0D5001A66F9 /* PasskeysSwiftUI */, + 8B41F65F2DEDD0D6001A66F9 /* PasskeysSwiftUIUITests */, + 8B41F6812DEDD23B001A66F9 /* Frameworks */, + 8B41F6462DEDD0D5001A66F9 /* Products */, + ); + sourceTree = ""; + }; + 8B41F6462DEDD0D5001A66F9 /* Products */ = { + isa = PBXGroup; + children = ( + 8B41F6452DEDD0D5001A66F9 /* PasskeysSwiftUI.app */, + 8B41F65C2DEDD0D6001A66F9 /* PasskeysSwiftUI.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 8B41F6812DEDD23B001A66F9 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8B41F6442DEDD0D5001A66F9 /* PasskeysSwiftUI */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8B41F6662DEDD0D6001A66F9 /* Build configuration list for PBXNativeTarget "PasskeysSwiftUI" */; + buildPhases = ( + 8B41F6412DEDD0D5001A66F9 /* Sources */, + 8B41F6422DEDD0D5001A66F9 /* Frameworks */, + 8B41F6432DEDD0D5001A66F9 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 8B41F6472DEDD0D5001A66F9 /* PasskeysSwiftUI */, + 8BD71C052DEE41D800CEDD92 /* Config */, + ); + name = PasskeysSwiftUI; + packageProductDependencies = ( + 81158D7A074F1B72DD4C07DE /* PingJourney */, + 9076D702DE7ED6A5139E4106 /* PingFido */, + 06B04414648276D65C9BDF68 /* PingOidc */, + 4A04B7FD6BA345628F49F01E /* PingOrchestrate */, + E256473691CDBA1288A61EF4 /* PingLogger */, + 62EB33A1CFDCF3A274C93942 /* PingJourneyPlugin */, + ); + productName = PasskeysSwiftUI; + productReference = 8B41F6452DEDD0D5001A66F9 /* PasskeysSwiftUI.app */; + productType = "com.apple.product-type.application"; + }; + 8B41F65B2DEDD0D6001A66F9 /* PasskeysSwiftUIUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8B41F66C2DEDD0D6001A66F9 /* Build configuration list for PBXNativeTarget "PasskeysSwiftUIUITests" */; + buildPhases = ( + 8B41F6582DEDD0D6001A66F9 /* Sources */, + 8B41F6592DEDD0D6001A66F9 /* Frameworks */, + 8B41F65A2DEDD0D6001A66F9 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 8B41F65E2DEDD0D6001A66F9 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 8B41F65F2DEDD0D6001A66F9 /* PasskeysSwiftUIUITests */, + ); + name = PasskeysSwiftUIUITests; + productName = PasskeysSwiftUIUITests; + productReference = 8B41F65C2DEDD0D6001A66F9 /* PasskeysSwiftUI.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 8B41F63D2DEDD0D5001A66F9 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1630; + LastUpgradeCheck = 1630; + TargetAttributes = { + 8B41F6442DEDD0D5001A66F9 = { + CreatedOnToolsVersion = 16.3; + }; + 8B41F65B2DEDD0D6001A66F9 = { + CreatedOnToolsVersion = 16.3; + TestTargetID = 8B41F6442DEDD0D5001A66F9; + }; + }; + }; + buildConfigurationList = 8B41F6402DEDD0D5001A66F9 /* Build configuration list for PBXProject "PasskeysSwiftUI" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 8B41F63C2DEDD0D5001A66F9; + minimizedProjectReferenceProxies = 1; + packageReferences = ( + 2E4B7E570803C45A04F1658C /* XCRemoteSwiftPackageReference "ping-ios-sdk" */, + ); + preferredProjectObjectVersion = 77; + productRefGroup = 8B41F6462DEDD0D5001A66F9 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8B41F6442DEDD0D5001A66F9 /* PasskeysSwiftUI */, + 8B41F65B2DEDD0D6001A66F9 /* PasskeysSwiftUIUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8B41F6432DEDD0D5001A66F9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8B41F65A2DEDD0D6001A66F9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8B41F6412DEDD0D5001A66F9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8B41F6582DEDD0D6001A66F9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 8B41F65E2DEDD0D6001A66F9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8B41F6442DEDD0D5001A66F9 /* PasskeysSwiftUI */; + targetProxy = 8B41F65D2DEDD0D6001A66F9 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 8B41F6642DEDD0D6001A66F9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.4; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 8B41F6652DEDD0D6001A66F9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.4; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_VERSION = 5.0; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 8B41F6672DEDD0D6001A66F9 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReferenceAnchor = 8BD71C052DEE41D800CEDD92 /* Config */; + baseConfigurationReferenceRelativePath = Debug.xcconfig; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 9QSE66762D; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = PasskeysSwiftUI; + INFOPLIST_KEY_NSFaceIDUsageDescription = "This app uses Face ID for biometric authentication with passkeys."; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeRight UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationPortrait"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeRight UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.forgerock.unsummit; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 8B41F6682DEDD0D6001A66F9 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReferenceAnchor = 8BD71C052DEE41D800CEDD92 /* Config */; + baseConfigurationReferenceRelativePath = Release.xcconfig; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 9QSE66762D; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = PasskeysSwiftUI; + INFOPLIST_KEY_NSFaceIDUsageDescription = "This app uses Face ID for biometric authentication with passkeys."; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeRight UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationPortrait"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeRight UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.forgerock.unsummit; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 8B41F66D2DEDD0D6001A66F9 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReferenceAnchor = 8BD71C052DEE41D800CEDD92 /* Config */; + baseConfigurationReferenceRelativePath = Tests.xcconfig; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + GENERATE_INFOPLIST_FILE = YES; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = PasskeysSwiftUI; + }; + name = Debug; + }; + 8B41F66E2DEDD0D6001A66F9 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReferenceAnchor = 8BD71C052DEE41D800CEDD92 /* Config */; + baseConfigurationReferenceRelativePath = Tests.xcconfig; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + GENERATE_INFOPLIST_FILE = YES; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = PasskeysSwiftUI; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 8B41F6402DEDD0D5001A66F9 /* Build configuration list for PBXProject "PasskeysSwiftUI" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8B41F6642DEDD0D6001A66F9 /* Debug */, + 8B41F6652DEDD0D6001A66F9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8B41F6662DEDD0D6001A66F9 /* Build configuration list for PBXNativeTarget "PasskeysSwiftUI" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8B41F6672DEDD0D6001A66F9 /* Debug */, + 8B41F6682DEDD0D6001A66F9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8B41F66C2DEDD0D6001A66F9 /* Build configuration list for PBXNativeTarget "PasskeysSwiftUIUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8B41F66D2DEDD0D6001A66F9 /* Debug */, + 8B41F66E2DEDD0D6001A66F9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 2E4B7E570803C45A04F1658C /* XCRemoteSwiftPackageReference "ping-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ForgeRock/ping-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 06B04414648276D65C9BDF68 /* PingOidc */ = { + isa = XCSwiftPackageProductDependency; + package = 2E4B7E570803C45A04F1658C /* XCRemoteSwiftPackageReference "ping-ios-sdk" */; + productName = PingOidc; + }; + 4A04B7FD6BA345628F49F01E /* PingOrchestrate */ = { + isa = XCSwiftPackageProductDependency; + package = 2E4B7E570803C45A04F1658C /* XCRemoteSwiftPackageReference "ping-ios-sdk" */; + productName = PingOrchestrate; + }; + 62EB33A1CFDCF3A274C93942 /* PingJourneyPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = 2E4B7E570803C45A04F1658C /* XCRemoteSwiftPackageReference "ping-ios-sdk" */; + productName = PingJourneyPlugin; + }; + 81158D7A074F1B72DD4C07DE /* PingJourney */ = { + isa = XCSwiftPackageProductDependency; + package = 2E4B7E570803C45A04F1658C /* XCRemoteSwiftPackageReference "ping-ios-sdk" */; + productName = PingJourney; + }; + 9076D702DE7ED6A5139E4106 /* PingFido */ = { + isa = XCSwiftPackageProductDependency; + package = 2E4B7E570803C45A04F1658C /* XCRemoteSwiftPackageReference "ping-ios-sdk" */; + productName = PingFido; + }; + E256473691CDBA1288A61EF4 /* PingLogger */ = { + isa = XCSwiftPackageProductDependency; + package = 2E4B7E570803C45A04F1658C /* XCRemoteSwiftPackageReference "ping-ios-sdk" */; + productName = PingLogger; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 8B41F63D2DEDD0D5001A66F9 /* Project object */; +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000..c1f9eb91 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,114 @@ +{ + "originHash" : "956d622730eea977a9714ff7bca8e26297309c8b894a7bbf03c3f9f9b8d6d312", + "pins" : [ + { + "identity" : "app-check", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/app-check.git", + "state" : { + "revision" : "61b85103a1aeed8218f17c794687781505fbbef5", + "version" : "11.2.0" + } + }, + { + "identity" : "appauth-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/openid/AppAuth-iOS.git", + "state" : { + "revision" : "145104f5ea9d58ae21b60add007c33c1cc0c948e", + "version" : "2.0.0" + } + }, + { + "identity" : "facebook-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/facebook/facebook-ios-sdk.git", + "state" : { + "revision" : "3fe31c168903759de1c5752d12856c5c437c6862", + "version" : "16.3.1" + } + }, + { + "identity" : "googlesignin-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleSignIn-iOS.git", + "state" : { + "revision" : "3996d908c7b3ce8a87d39c808f9a6b2a08fbe043", + "version" : "9.0.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "60da361632d0de02786f709bdc0c4df340f7613e", + "version" : "8.1.0" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b", + "version" : "3.5.0" + } + }, + { + "identity" : "gtmappauth", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GTMAppAuth.git", + "state" : { + "revision" : "56e0ccf09a6dd29dc7e68bdf729598240ca8aa16", + "version" : "5.0.0" + } + }, + { + "identity" : "interop-ios-for-google-sdks", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/interop-ios-for-google-sdks.git", + "state" : { + "revision" : "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe", + "version" : "101.0.0" + } + }, + { + "identity" : "ping-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ForgeRock/ping-ios-sdk", + "state" : { + "revision" : "d025874d598d7337a72f0a8ee446dcddae7b6ef2", + "version" : "2.0.0" + } + }, + { + "identity" : "pingone-signals-sdk-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pingidentity/pingone-signals-sdk-ios.git", + "state" : { + "revision" : "e41f7070fdbb43dd7762274ec610c099256a7c7e", + "version" : "5.4.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", + "version" : "2.4.0" + } + }, + { + "identity" : "recaptcha-enterprise-mobile-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/GoogleCloudPlatform/recaptcha-enterprise-mobile-sdk.git", + "state" : { + "revision" : "fb634e89a36fd91725ad654d5576d800c061b37d", + "version" : "18.8.2" + } + } + ], + "version" : 3 +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI.xcodeproj/xcshareddata/xcschemes/PasskeysSwiftUI.xcscheme b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI.xcodeproj/xcshareddata/xcschemes/PasskeysSwiftUI.xcscheme new file mode 100644 index 00000000..6190055b --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI.xcodeproj/xcshareddata/xcschemes/PasskeysSwiftUI.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/App/PasskeysApp.swift b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/App/PasskeysApp.swift new file mode 100644 index 00000000..ae9e3f43 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/App/PasskeysApp.swift @@ -0,0 +1,20 @@ +// +// PasskeysApp.swift +// PasskeysSwiftUI +// +// Copyright (c) 2026 Ping Identity Corporation. All rights reserved. +// +// This software may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// + +import SwiftUI + +@main +struct PasskeysApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Assets.xcassets/AccentColor.colorset/Contents.json b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Assets.xcassets/AppIcon.appiconset/Contents.json b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..23058801 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Assets.xcassets/Contents.json b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Assets.xcassets/Logo.imageset/Contents.json b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Assets.xcassets/Logo.imageset/Contents.json new file mode 100644 index 00000000..1acb1006 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Assets.xcassets/Logo.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Ping Identity Logo.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Assets.xcassets/Logo.imageset/Ping Identity Logo.png b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Assets.xcassets/Logo.imageset/Ping Identity Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2218aa29963e4ed5dba6ae2f2c0f0fed5d0705b9 GIT binary patch literal 23323 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 Sl%E7?k=@djOYT^6BmWmd$y(?D literal 0 HcmV?d00001 diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Config/AppConfiguration.swift b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Config/AppConfiguration.swift new file mode 100644 index 00000000..a6c30463 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Config/AppConfiguration.swift @@ -0,0 +1,57 @@ +// +// AppConfiguration.swift +// PasskeysSwiftUI +// +// Copyright (c) 2026 Ping Identity Corporation. All rights reserved. +// +// This software may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// + +import Foundation +import PingJourney +import PingStorage +import PingLogger + +enum JourneyName { + static let login = "Login" + static let fidoRegistration = "BlogWebAuthnRegistration" + static let fidoAuthentication = "BlogWebAuthnAuthentication" +} + +enum UserDefaultsKey { + static let biometricsEnabled = "BiometricsEnabled" +} + +enum ServerConfig { + static let serverUrl = "https://openam-bafaloukas.forgeblocks.com/am" + static let realm = "alpha" + static let cookieName = "386c0d288cac4b9" + static let clientId = "iosClient" + static let scopes: Set = ["openid", "profile", "email", "address"] + static let redirectUri = "frauth://com.forgerock.ios.frexample" + static let discoveryEndpoint = "https://openam-bafaloukas.forgeblocks.com/am/oauth2/alpha/.well-known/openid-configuration" +} + +@MainActor +class AppJourney { + static let shared = AppJourney() + + let journey: Journey + + private init() { + journey = Journey.createJourney { journeyConfig in + journeyConfig.serverUrl = ServerConfig.serverUrl + journeyConfig.realm = ServerConfig.realm + journeyConfig.cookie = ServerConfig.cookieName + journeyConfig.logger = LogManager.standard + journeyConfig.module(PingJourney.OidcModule.config) { oidcConfig in + oidcConfig.clientId = ServerConfig.clientId + oidcConfig.scopes = ServerConfig.scopes + oidcConfig.redirectUri = ServerConfig.redirectUri + oidcConfig.discoveryEndpoint = ServerConfig.discoveryEndpoint + oidcConfig.logger = LogManager.standard + } + } + } +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/PasskeysSwiftUI.xctestplan b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/PasskeysSwiftUI.xctestplan new file mode 100644 index 00000000..b6272f7a --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/PasskeysSwiftUI.xctestplan @@ -0,0 +1,35 @@ +{ + "configurations" : [ + { + "id" : "24499A57-8A8C-49DD-9DF6-FD06943246D4", + "name" : "Test Scheme Action", + "options" : { + + } + } + ], + "defaultOptions" : { + "targetForVariableExpansion" : { + "containerPath" : "container:PasskeysSwiftUI.xcodeproj", + "identifier" : "8B41F6442DEDD0D5001A66F9", + "name" : "PasskeysSwiftUI" + } + }, + "testTargets" : [ + { + "target" : { + "containerPath" : "container:PasskeysSwiftUIPackage", + "identifier" : "PasskeysSwiftUIFeatureTests", + "name" : "PasskeysSwiftUIFeatureTests" + } + }, + { + "target" : { + "containerPath" : "container:PasskeysSwiftUI.xcodeproj", + "identifier" : "8B41F65B2DEDD0D6001A66F9", + "name" : "PasskeysSwiftUIUITests" + } + } + ], + "version" : 1 +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/ViewModels/AuthenticatedViewModel.swift b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/ViewModels/AuthenticatedViewModel.swift new file mode 100644 index 00000000..230e2556 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/ViewModels/AuthenticatedViewModel.swift @@ -0,0 +1,45 @@ +// +// AuthenticatedViewModel.swift +// PasskeysSwiftUI +// +// Copyright (c) 2026 Ping Identity Corporation. All rights reserved. +// +// This software may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// + +import Observation +import PingJourney +import PingOidc + +@Observable +@MainActor +final class AuthenticatedViewModel { + var userInfo: [String: Any] = [:] + var isLoadingUserInfo = false + var userInfoError: String? + var isSignedOut = false + + func loadUserInfo() async { + isLoadingUserInfo = true + userInfoError = nil + defer { isLoadingUserInfo = false } + + guard let user = await AppJourney.shared.journey.journeyUser() else { + userInfoError = "No authenticated user found." + return + } + + switch await user.userinfo(cache: false) { + case .success(let info): + userInfo = info + case .failure(let error): + userInfoError = error.localizedDescription + } + } + + func signOut() async { + _ = await AppJourney.shared.journey.signOff() + isSignedOut = true + } +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/ViewModels/LoginViewModel.swift b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/ViewModels/LoginViewModel.swift new file mode 100644 index 00000000..7eccdb83 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/ViewModels/LoginViewModel.swift @@ -0,0 +1,44 @@ +// +// LoginViewModel.swift +// PasskeysSwiftUI +// +// Copyright (c) 2026 Ping Identity Corporation. All rights reserved. +// +// This software may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// + +import Foundation +import Observation +import PingJourney +import PingOrchestrate + +@Observable +@MainActor +final class LoginViewModel { + var node: Node? + var isLoading = false + + func start() async { + let journeyName = UserDefaults.standard.bool(forKey: UserDefaultsKey.biometricsEnabled) + ? JourneyName.fidoAuthentication + : JourneyName.login + await startJourney(name: journeyName) + } + + func startJourney(name: String) async { + isLoading = true + defer { isLoading = false } + node = await AppJourney.shared.journey.start(name) + } + + func next(continueNode: ContinueNode) async { + isLoading = true + defer { isLoading = false } + node = await continueNode.next() + } + + func reset() { + node = nil + } +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/ViewModels/RegistrationViewModel.swift b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/ViewModels/RegistrationViewModel.swift new file mode 100644 index 00000000..317290b7 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/ViewModels/RegistrationViewModel.swift @@ -0,0 +1,46 @@ +// +// RegistrationViewModel.swift +// PasskeysSwiftUI +// +// Copyright (c) 2026 Ping Identity Corporation. All rights reserved. +// +// This software may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// + +import Foundation +import Observation +import PingJourney +import PingOrchestrate + +@Observable +@MainActor +final class RegistrationViewModel { + var node: Node? + var isLoading = false + var isRegistered = false + var isFailure = false + + func startRegistration() async { + isLoading = true + isRegistered = false + isFailure = false + defer { isLoading = false } + node = await AppJourney.shared.journey.start(JourneyName.fidoRegistration) { options in + options.forceAuth = true + } + } + + func next(continueNode: ContinueNode) async { + isLoading = true + defer { isLoading = false } + let nextNode = await continueNode.next() + node = nextNode + if nextNode is SuccessNode { + UserDefaults.standard.set(true, forKey: UserDefaultsKey.biometricsEnabled) + isRegistered = true + } else if nextNode is FailureNode || nextNode is ErrorNode { + isFailure = true + } + } +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/AuthenticatedView.swift b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/AuthenticatedView.swift new file mode 100644 index 00000000..6351c3f1 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/AuthenticatedView.swift @@ -0,0 +1,182 @@ +// +// AuthenticatedView.swift +// PasskeysSwiftUI +// +// Copyright (c) 2026 Ping Identity Corporation. All rights reserved. +// +// This software may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// + +import SwiftUI +import PingJourney +import PingOrchestrate + +struct AuthenticatedView: View { + let successNode: SuccessNode + let onSignOut: () -> Void + + @State private var viewModel = AuthenticatedViewModel() + @State private var showSettings = false + + var body: some View { + NavigationStack { + ScrollView { + VStack(spacing: 0) { + AuthenticatedHeaderView() + + VStack(spacing: 20) { + profileSection + signOutButton + } + .padding(24) + } + } + .navigationBarTitleDisplayMode(.inline) + .toolbar { settingsToolbarItem } + .sheet(isPresented: $showSettings) { SettingsView() } + .onChange(of: viewModel.isSignedOut) { _, signedOut in + if signedOut { onSignOut() } + } + .task { + await viewModel.loadUserInfo() + } + } + } + + // MARK: - Profile Section + + @ViewBuilder + private var profileSection: some View { + if viewModel.isLoadingUserInfo { + ProgressView("Loading profile…") + .frame(maxWidth: .infinity) + .padding(32) + } else if let errorMessage = viewModel.userInfoError { + Text(errorMessage) + .font(.subheadline) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + .padding() + } else if !viewModel.userInfo.isEmpty { + UserInfoCard(userInfo: viewModel.userInfo) + } + } + + private var signOutButton: some View { + Button("Sign Out") { + Task { await viewModel.signOut() } + } + .buttonStyle(PingDestructiveButtonStyle()) + } + + private var settingsToolbarItem: some ToolbarContent { + ToolbarItem(placement: .navigationBarTrailing) { + Button { + showSettings = true + } label: { + Image(systemName: "gear") + .foregroundColor(.pingRed) + } + } + } +} + +// MARK: - Authenticated Header + +private struct AuthenticatedHeaderView: View { + var body: some View { + ZStack { + LinearGradient( + colors: [.pingRed, .pingRedDark], + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + + VStack(spacing: 12) { + Image("Logo") + .resizable() + .scaledToFit() + .frame(width: 80, height: 80) + + Image(systemName: "checkmark.shield.fill") + .font(.system(size: 36)) + .foregroundColor(.white.opacity(0.9)) + + Text("Authenticated") + .font(.system(size: 22, weight: .bold)) + .foregroundColor(.white) + } + .padding(.vertical, 32) + } + .ignoresSafeArea(edges: .top) + } +} + +// MARK: - User Info Card + +private struct UserInfoCard: View { + let userInfo: [String: Any] + + var body: some View { + VStack(alignment: .leading, spacing: 0) { + Text("Profile") + .font(.system(size: 13, weight: .semibold)) + .foregroundColor(.secondary) + .textCase(.uppercase) + .padding(.bottom, 10) + + VStack(spacing: 0) { + ForEach(userInfo.keys.sorted(), id: \.self) { key in + UserInfoRow(key: key, value: String(describing: userInfo[key] ?? "")) + } + } + .background(Color(.secondarySystemGroupedBackground)) + .cornerRadius(12) + } + } +} + +private struct UserInfoRow: View { + let key: String + let value: String + + var body: some View { + HStack(alignment: .top, spacing: 12) { + Text(key) + .font(.caption) + .fontWeight(.medium) + .foregroundColor(.secondary) + .frame(width: 110, alignment: .leading) + + Text(value) + .font(.caption) + .foregroundColor(.primary) + .multilineTextAlignment(.leading) + + Spacer() + } + .padding(.horizontal, 16) + .padding(.vertical, 10) + Divider() + .padding(.leading, 16) + } +} + +// MARK: - Destructive Button Style + +struct PingDestructiveButtonStyle: ButtonStyle { + func makeBody(configuration: Configuration) -> some View { + configuration.label + .font(.headline) + .foregroundColor(.pingRed) + .padding() + .frame(maxWidth: .infinity) + .frame(height: 50) + .background( + RoundedRectangle(cornerRadius: 15) + .stroke(Color.pingRed, lineWidth: 1.5) + .opacity(configuration.isPressed ? 0.6 : 1) + ) + } +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/Callbacks/FidoAuthenticationCallbackView.swift b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/Callbacks/FidoAuthenticationCallbackView.swift new file mode 100644 index 00000000..1b78969f --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/Callbacks/FidoAuthenticationCallbackView.swift @@ -0,0 +1,75 @@ +// +// FidoAuthenticationCallbackView.swift +// PasskeysSwiftUI +// +// Copyright (c) 2026 Ping Identity Corporation. All rights reserved. +// +// This software may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// + +import SwiftUI +import PingFido + +struct FidoAuthenticationCallbackView: View { + let callback: FidoAuthenticationCallback + let onNext: () -> Void + + @State private var errorMessage: String? + @State private var isAuthenticating = false + + var body: some View { + VStack(spacing: 20) { + FidoIconView(systemName: "touchid", tint: .pingRed) + + VStack(spacing: 6) { + Text("Biometric Authentication") + .font(.title2) + .fontWeight(.semibold) + + Text("Use your passkey to sign in securely.") + .font(.subheadline) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + } + + if let errorMessage { + ErrorMessageView(message: errorMessage) + } + + Button(action: authenticate) { + if isAuthenticating { + ProgressView() + .tint(.white) + } else { + Text("Authenticate with Passkey") + } + } + .buttonStyle(PingPrimaryButtonStyle()) + .disabled(isAuthenticating) + } + } + + private func authenticate() { + Task { + isAuthenticating = true + defer { isAuthenticating = false } + + guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, + let window = windowScene.windows.first else { + errorMessage = "Unable to find active window." + return + } + + let result = await callback.authenticate(window: window) + + switch result { + case .success: + onNext() + case .failure(let error): + errorMessage = error.localizedDescription + onNext() + } + } + } +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/Callbacks/FidoRegistrationCallbackView.swift b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/Callbacks/FidoRegistrationCallbackView.swift new file mode 100644 index 00000000..5e4d37d0 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/Callbacks/FidoRegistrationCallbackView.swift @@ -0,0 +1,79 @@ +// +// FidoRegistrationCallbackView.swift +// PasskeysSwiftUI +// +// Copyright (c) 2026 Ping Identity Corporation. All rights reserved. +// +// This software may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// + +import SwiftUI +import PingFido + +struct FidoRegistrationCallbackView: View { + let callback: FidoRegistrationCallback + let onNext: () -> Void + + @State private var deviceName = UIDevice.current.name + @State private var errorMessage: String? + @State private var isRegistering = false + + var body: some View { + VStack(spacing: 20) { + FidoIconView(systemName: "faceid", tint: .pingRed) + + VStack(spacing: 6) { + Text("Register Passkey") + .font(.title2) + .fontWeight(.semibold) + + Text("Create a passkey for fast, secure biometric login.") + .font(.subheadline) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + } + + PingTextField(placeholder: "Device Name", text: $deviceName) + + if let errorMessage { + ErrorMessageView(message: errorMessage) + } + + Button(action: register) { + if isRegistering { + ProgressView() + .tint(.white) + } else { + Text("Register with Passkey") + } + } + .buttonStyle(PingPrimaryButtonStyle()) + .disabled(isRegistering) + } + } + + private func register() { + Task { + isRegistering = true + defer { isRegistering = false } + + guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, + let window = windowScene.windows.first else { + errorMessage = "Unable to find active window." + return + } + + let name = deviceName.isEmpty ? nil : deviceName + let result = await callback.register(deviceName: name, window: window) + + switch result { + case .success: + onNext() + case .failure(let error): + errorMessage = error.localizedDescription + onNext() + } + } + } +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/Callbacks/NameCallbackView.swift b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/Callbacks/NameCallbackView.swift new file mode 100644 index 00000000..a82007f1 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/Callbacks/NameCallbackView.swift @@ -0,0 +1,26 @@ +// +// NameCallbackView.swift +// PasskeysSwiftUI +// +// Copyright (c) 2026 Ping Identity Corporation. All rights reserved. +// +// This software may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// + +import SwiftUI +import PingJourney + +struct NameCallbackView: View { + let callback: NameCallback + @State private var username = "" + + var body: some View { + PingTextField( + placeholder: callback.prompt.isEmpty ? "Username" : callback.prompt, + text: $username, + contentType: .username + ) + .onChange(of: username) { callback.name = username } + } +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/Callbacks/PasswordCallbackView.swift b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/Callbacks/PasswordCallbackView.swift new file mode 100644 index 00000000..1665e041 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/Callbacks/PasswordCallbackView.swift @@ -0,0 +1,25 @@ +// +// PasswordCallbackView.swift +// PasskeysSwiftUI +// +// Copyright (c) 2026 Ping Identity Corporation. All rights reserved. +// +// This software may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// + +import SwiftUI +import PingJourney + +struct PasswordCallbackView: View { + let callback: PasswordCallback + @State private var password = "" + + var body: some View { + PingSecureField( + placeholder: callback.prompt.isEmpty ? "Password" : callback.prompt, + text: $password + ) + .onChange(of: password) { callback.password = password } + } +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/Components/Theme.swift b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/Components/Theme.swift new file mode 100644 index 00000000..2e76cb05 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/Components/Theme.swift @@ -0,0 +1,189 @@ +// +// Theme.swift +// PasskeysSwiftUI +// +// Copyright (c) 2026 Ping Identity Corporation. All rights reserved. +// +// This software may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// + +import SwiftUI + +extension Color { + static let pingRed = Color(red: 163.0 / 255.0, green: 19.0 / 255.0, blue: 0.0 / 255.0) + static let pingRedDark = Color(red: 0.6, green: 0.1, blue: 0.1) + static let pingTextField = Color(red: 220.0 / 255.0, green: 230.0 / 255.0, blue: 230.0 / 255.0) +} + +// MARK: - Primary Action Button + +struct PingPrimaryButtonStyle: ButtonStyle { + func makeBody(configuration: Configuration) -> some View { + configuration.label + .font(.headline) + .foregroundColor(.white) + .padding() + .frame(maxWidth: .infinity) + .frame(height: 50) + .background(Color.pingRed.opacity(configuration.isPressed ? 0.8 : 1)) + .cornerRadius(15) + .shadow(color: .black.opacity(0.2), radius: 10, x: 0, y: 4) + } +} + +// MARK: - Branded Header + +struct PingHeaderView: View { + var body: some View { + ZStack { + LinearGradient( + colors: [.pingRed, .pingRedDark], + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + + VStack(spacing: 12) { + Image("Logo") + .resizable() + .scaledToFit() + .frame(width: 80, height: 80) + + Text("Passkeys Demo") + .font(.system(size: 24, weight: .bold)) + .foregroundColor(.white) + + Text("Secure biometric authentication") + .font(.system(size: 14, weight: .medium)) + .foregroundColor(.white.opacity(0.85)) + } + .padding(.vertical, 32) + } + .ignoresSafeArea(edges: .top) + } +} + +// MARK: - Branded Text Field + +struct PingTextField: View { + let placeholder: String + @Binding var text: String + var contentType: UITextContentType? = nil + var autocapitalization: TextInputAutocapitalization = .never + + var body: some View { + TextField(placeholder, text: $text) + .textContentType(contentType) + .autocorrectionDisabled() + .textInputAutocapitalization(autocapitalization) + .padding(12) + .background( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.pingTextField, lineWidth: 1.5) + ) + } +} + +// MARK: - Branded Secure Field + +struct PingSecureField: View { + let placeholder: String + @Binding var text: String + @State private var isSecure = true + + var body: some View { + HStack { + Group { + if isSecure { + SecureField(placeholder, text: $text) + } else { + TextField(placeholder, text: $text) + } + } + .textContentType(.password) + .autocorrectionDisabled() + .textInputAutocapitalization(.never) + + Button { + isSecure.toggle() + } label: { + Image(systemName: isSecure ? "eye.slash" : "eye") + .foregroundColor(.pingRed) + .frame(width: 20, height: 20) + } + } + .padding(12) + .background( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.pingTextField, lineWidth: 1.5) + ) + } +} + +// MARK: - FIDO Icon View + +struct FidoIconView: View { + let systemName: String + let tint: Color + + var body: some View { + ZStack { + Circle() + .fill(tint.opacity(0.12)) + .frame(width: 88, height: 88) + + Image(systemName: systemName) + .font(.system(size: 40)) + .foregroundColor(tint) + } + } +} + +// MARK: - Error Message View + +struct ErrorMessageView: View { + let message: String + + var body: some View { + HStack(spacing: 6) { + Image(systemName: "exclamationmark.circle.fill") + .font(.caption) + Text(message) + .font(.caption) + .multilineTextAlignment(.leading) + } + .foregroundColor(.red) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 4) + } +} + +// MARK: - Loading Overlay + +struct LoadingOverlay: View { + let message: String + + init(_ message: String = "Loading…") { + self.message = message + } + + var body: some View { + ZStack { + Color.black.opacity(0.35) + .ignoresSafeArea() + + VStack(spacing: 12) { + ProgressView() + .progressViewStyle(.circular) + .tint(.white) + .scaleEffect(1.4) + + Text(message) + .font(.subheadline) + .foregroundColor(.white) + } + .padding(24) + .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 16)) + } + } +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/ContentView.swift b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/ContentView.swift new file mode 100644 index 00000000..967410c8 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/ContentView.swift @@ -0,0 +1,84 @@ +// +// ContentView.swift +// PasskeysSwiftUI +// +// Copyright (c) 2026 Ping Identity Corporation. All rights reserved. +// +// This software may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// + +import SwiftUI +import PingOrchestrate +import PingJourney + +struct ContentView: View { + @State private var loginViewModel = LoginViewModel() + + var body: some View { + Group { + switch loginViewModel.node { + case let successNode as SuccessNode: + AuthenticatedView(successNode: successNode, onSignOut: loginViewModel.reset) + case let failureNode as FailureNode: + ErrorView(message: failureNode.cause.localizedDescription, onRetry: retry) + case let errorNode as ErrorNode: + ErrorView( + message: errorNode.message.isEmpty ? "An error occurred." : errorNode.message, + onRetry: retry + ) + case let continueNode as ContinueNode: + LoginView( + continueNode: continueNode, + isLoading: loginViewModel.isLoading, + onNext: { Task { await loginViewModel.next(continueNode: continueNode) } } + ) + default: + LoginView( + continueNode: nil, + isLoading: loginViewModel.isLoading, + onNext: { Task { await loginViewModel.start() } } + ) + } + } + .task { + await loginViewModel.start() + } + } + + private func retry() { + loginViewModel.reset() + Task { await loginViewModel.start() } + } +} + +// MARK: - Error View + +private struct ErrorView: View { + let message: String + let onRetry: () -> Void + + var body: some View { + VStack(spacing: 24) { + Image(systemName: "exclamationmark.triangle.fill") + .font(.system(size: 56)) + .foregroundColor(.pingRed) + + VStack(spacing: 8) { + Text("Something went wrong") + .font(.title3) + .fontWeight(.semibold) + + Text(message) + .font(.subheadline) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + } + + Button("Try Again", action: onRetry) + .buttonStyle(PingPrimaryButtonStyle()) + .padding(.horizontal, 40) + } + .padding(32) + } +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/ContinueNodeView.swift b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/ContinueNodeView.swift new file mode 100644 index 00000000..92c90d54 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/ContinueNodeView.swift @@ -0,0 +1,61 @@ +// +// ContinueNodeView.swift +// PasskeysSwiftUI +// +// Copyright (c) 2026 Ping Identity Corporation. All rights reserved. +// +// This software may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// + +import SwiftUI +import PingJourney +import PingJourneyPlugin +import PingOrchestrate +import PingFido + +struct ContinueNodeView: View { + let node: ContinueNode + let onNext: () -> Void + + var body: some View { + VStack(spacing: 16) { + ForEach(node.callbacks, id: \.id) { callback in + callbackView(for: callback) + } + + if !hasSelfAdvancingCallback { + Button("Next", action: onNext) + .buttonStyle(PingPrimaryButtonStyle()) + } + } + } + + // FIDO callbacks advance the node themselves after the OS prompt completes, + // so we suppress the generic "Next" button when they are present. + private var hasSelfAdvancingCallback: Bool { + node.callbacks.contains { $0 is FidoRegistrationCallback || $0 is FidoAuthenticationCallback } + } + + @ViewBuilder + private func callbackView(for callback: any Callback) -> some View { + switch callback { + case let nameCallback as NameCallback: + NameCallbackView(callback: nameCallback) + case let passwordCallback as PasswordCallback: + PasswordCallbackView(callback: passwordCallback) + case let textOutputCallback as TextOutputCallback: + Text(textOutputCallback.message) + .font(.subheadline) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + .padding(.vertical, 4) + case let fidoRegistrationCallback as FidoRegistrationCallback: + FidoRegistrationCallbackView(callback: fidoRegistrationCallback, onNext: onNext) + case let fidoAuthenticationCallback as FidoAuthenticationCallback: + FidoAuthenticationCallbackView(callback: fidoAuthenticationCallback, onNext: onNext) + default: + EmptyView() + } + } +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/LoginView.swift b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/LoginView.swift new file mode 100644 index 00000000..1a4c8bd5 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/LoginView.swift @@ -0,0 +1,64 @@ +// +// LoginView.swift +// PasskeysSwiftUI +// +// Copyright (c) 2026 Ping Identity Corporation. All rights reserved. +// +// This software may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// + +import SwiftUI +import PingJourney +import PingOrchestrate + +struct LoginView: View { + let continueNode: ContinueNode? + let isLoading: Bool + let onNext: () -> Void + + var body: some View { + ZStack { + ScrollView { + VStack(spacing: 0) { + PingHeaderView() + + VStack(spacing: 24) { + if let continueNode { + ContinueNodeView(node: continueNode, onNext: onNext) + } else { + SignInPromptView(onSignIn: onNext) + } + } + .padding(24) + } + } + + if isLoading { + LoadingOverlay("Signing in…") + } + } + } +} + +// MARK: - Sign In Prompt + +private struct SignInPromptView: View { + let onSignIn: () -> Void + + var body: some View { + VStack(spacing: 16) { + Text("Welcome back") + .font(.title2) + .fontWeight(.semibold) + + Text("Sign in with your credentials or use a passkey for biometric authentication.") + .font(.subheadline) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + + Button("Sign In", action: onSignIn) + .buttonStyle(PingPrimaryButtonStyle()) + } + } +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/SettingsView.swift b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/SettingsView.swift new file mode 100644 index 00000000..8a83526c --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Views/SettingsView.swift @@ -0,0 +1,106 @@ +// +// SettingsView.swift +// PasskeysSwiftUI +// +// Copyright (c) 2026 Ping Identity Corporation. All rights reserved. +// +// This software may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// + +import SwiftUI +import PingJourney +import PingOrchestrate + +struct SettingsView: View { + @Environment(\.dismiss) private var dismiss + @State private var registrationViewModel = RegistrationViewModel() + @State private var biometricsEnabled = UserDefaults.standard.bool(forKey: UserDefaultsKey.biometricsEnabled) + + var body: some View { + NavigationStack { + ZStack { + settingsForm + if registrationViewModel.isLoading { + LoadingOverlay("Registering passkey…") + } + } + .navigationTitle("Settings") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button("Done") { dismiss() } + .foregroundColor(.pingRed) + } + } + .onChange(of: registrationViewModel.isRegistered) { _, registered in + if registered { dismiss() } + } + .onChange(of: registrationViewModel.isFailure) { _, isFailure in + if isFailure { biometricsEnabled = false } + } + } + } + + // MARK: - Settings Form + + private var settingsForm: some View { + Form { + biometricsSection + registrationSection + errorSection + } + .tint(.pingRed) + } + + private var biometricsSection: some View { + Section { + Toggle("Biometric / Passkey Login", isOn: $biometricsEnabled) + .onChange(of: biometricsEnabled) { _, isEnabled in + if isEnabled { + Task { await registrationViewModel.startRegistration() } + } else { + UserDefaults.standard.set(false, forKey: UserDefaultsKey.biometricsEnabled) + } + } + } header: { + Text("Authentication") + } footer: { + Text(biometricsEnabled + ? "Passkey login is enabled. You will be prompted with Face ID or Touch ID on the next sign-in." + : "Enable to register a passkey for biometric login.") + } + } + + @ViewBuilder + private var registrationSection: some View { + if let continueNode = registrationViewModel.node as? ContinueNode { + Section("Passkey Registration") { + ContinueNodeView(node: continueNode, onNext: { + Task { await registrationViewModel.next(continueNode: continueNode) } + }) + .padding(.vertical, 8) + } + } + } + + @ViewBuilder + private var errorSection: some View { + if let failureNode = registrationViewModel.node as? FailureNode { + Section { + Label(failureNode.cause.localizedDescription, systemImage: "xmark.circle.fill") + .font(.subheadline) + .foregroundColor(.red) + } + } else if let errorNode = registrationViewModel.node as? ErrorNode { + Section { + Label( + errorNode.message.isEmpty ? "An error occurred during registration." : errorNode.message, + systemImage: "xmark.circle.fill" + ) + .font(.subheadline) + .foregroundColor(.red) + } + } + } +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUIUITests/PasskeysSwiftUIUITests.swift b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUIUITests/PasskeysSwiftUIUITests.swift new file mode 100644 index 00000000..d14ba0d9 --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUIUITests/PasskeysSwiftUIUITests.swift @@ -0,0 +1,26 @@ +import XCTest + +final class PasskeysSwiftUIUITests: XCTestCase { + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + @MainActor + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use XCTAssert and related functions to verify your tests produce the correct results. + XCTAssertTrue(true) + } +} diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/README.md b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/README.md new file mode 100644 index 00000000..89279f2a --- /dev/null +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/README.md @@ -0,0 +1,82 @@ +

+ + Ping Identity Logo + +


+

+ +# Passkeys Sample App using Swift/SwiftUI + +Ping provides these iOS samples to help demonstrate SDK functionality/implementation. They are provided "as is" and are not official products of Ping and are not officially supported. + +### Integrate with PingAM/AIC using Journeys and Passkeys: + +An example iOS project written in Swift/SwiftUI using the Ping iOS SDK, showcasing how to protect an application using Journeys and Passkeys. Based on the ["Set up passwordless authentication with Passkeys"](https://docs.pingidentity.com/sdks/latest/sdks/use-cases/how-to-go-passwordless-with-passkeys.html) blog post. + +## Requirements + +- Xcode: Latest version recommended +- iOS 18.0 or higher +- A physical iOS device or simulator +- PingAM/AIC server configured with the following journeys: + - `Login` — standard username and password authentication + - `BlogWebAuthnRegistration` — passkey registration + - `BlogWebAuthnAuthentication` — passkey-based sign-in + +## Getting Started + +To try out this sample, perform the following steps: + +1. Configure Ping Services + Ensure you have a PingAM/AIC server set up with the required authentication journeys and an OAuth 2.0 application for native mobile apps. See the [documentation](https://docs.pingidentity.com/sdks/latest/sdks/serverconfiguration/pingone/create-oauth2-client.html) for details. + +2. Clone this repo: + + ``` + git clone https://github.com/ForgeRock/sdk-sample-apps.git + ``` + +3. Open the workspace in Xcode: + + ``` + open iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI.xcworkspace + ``` + +4. Open `PasskeysSwiftUI/Config/AppConfiguration.swift` and update `ServerConfig` with your environment values: + + ```swift + static let serverUrl = "https://your-server.example.com/am" + static let realm = "alpha" + static let cookieName = "your-cookie-name" + static let clientId = "your-client-id" + static let redirectUri = "yourapp://callback" + static let discoveryEndpoint = "https://your-server.example.com/am/oauth2/alpha/.well-known/openid-configuration" + ``` + +5. Update the Associated Domains entitlement in `Config/PasskeysSwiftUI.entitlements` to match your server domain (required for the WebAuthn relying party ID): + + ```xml + com.apple.developer.associated-domains + + webcredentials:your-server.example.com + webcredentials:your-server.example.com?mode=develop + + ``` + +6. Build and run the app on a physical iOS device. + +## Features + +This sample demonstrates: + +- Journey-based authentication with callback handling (username/password) +- FIDO2/WebAuthn passkey registration via `FidoRegistrationCallback` +- FIDO2/WebAuthn passkey authentication via `FidoAuthenticationCallback` +- Conditional login flow — automatically uses passkey sign-in when a passkey is registered +- Session management and sign-out via the Journey SDK +- OIDC user info display after successful authentication + +## Additional Resources + +- Ping SDK Documentation: https://docs.pingidentity.com/sdks/latest/sdks/index.html +- Passwordless with Passkeys blog: https://docs.pingidentity.com/sdks/latest/sdks/use-cases/how-to-go-passwordless-with-passkeys.html From 62ae8dbad072727c64f397f9c013a0a99ca9925f Mon Sep 17 00:00:00 2001 From: "george.bafaloukas" Date: Thu, 7 May 2026 15:12:26 +0100 Subject: [PATCH 2/2] Remove hardcoded environment values from SwiftUI passkeys sample Replace personal server URL, cookie name, client ID, redirect URI, and associated domain entitlements with placeholder tokens so the sample is safe to share publicly. Co-Authored-By: Claude Sonnet 4.6 --- .../Config/PasskeysSwiftUI.entitlements | 4 ++-- .../PasskeysSwiftUI/Config/AppConfiguration.swift | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/PasskeysSwiftUI.entitlements b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/PasskeysSwiftUI.entitlements index 96bececb..06ee6669 100644 --- a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/PasskeysSwiftUI.entitlements +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/Config/PasskeysSwiftUI.entitlements @@ -4,8 +4,8 @@ com.apple.developer.associated-domains - webcredentials:openam-bafaloukas.forgeblocks.com?mode=develop - webcredentials:openam-bafaloukas.forgeblocks.com + webcredentials:your-server-domain.com?mode=develop + webcredentials:your-server-domain.com diff --git a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Config/AppConfiguration.swift b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Config/AppConfiguration.swift index a6c30463..ef55f668 100644 --- a/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Config/AppConfiguration.swift +++ b/iOS/swiftui-journey-module-passkeys/PasskeysSwiftUI/PasskeysSwiftUI/Config/AppConfiguration.swift @@ -24,13 +24,13 @@ enum UserDefaultsKey { } enum ServerConfig { - static let serverUrl = "https://openam-bafaloukas.forgeblocks.com/am" - static let realm = "alpha" - static let cookieName = "386c0d288cac4b9" - static let clientId = "iosClient" + static let serverUrl = "<#YOUR_SERVER_URL#>" // e.g. "https://your-tenant.forgeblocks.com/am" + static let realm = "<#YOUR_REALM#>" // e.g. "alpha" + static let cookieName = "<#YOUR_COOKIE_NAME#>" // Session cookie name from your AM config + static let clientId = "<#YOUR_CLIENT_ID#>" // OAuth 2.0 client ID static let scopes: Set = ["openid", "profile", "email", "address"] - static let redirectUri = "frauth://com.forgerock.ios.frexample" - static let discoveryEndpoint = "https://openam-bafaloukas.forgeblocks.com/am/oauth2/alpha/.well-known/openid-configuration" + static let redirectUri = "<#YOUR_REDIRECT_URI#>" // Must match your OAuth 2.0 client config + static let discoveryEndpoint = "<#YOUR_DISCOVERY_ENDPOINT#>" // e.g. "https://your-tenant.forgeblocks.com/am/oauth2//.well-known/openid-configuration" } @MainActor