Skip to content

feat: browser auth bridge impl#31

Open
jaredperreault-okta wants to merge 51 commits into
jp-react-native-sdkfrom
jp-rn-browser-session
Open

feat: browser auth bridge impl#31
jaredperreault-okta wants to merge 51 commits into
jp-react-native-sdkfrom
jp-rn-browser-session

Conversation

@jaredperreault-okta
Copy link
Copy Markdown
Contributor

No description provided.

url: string,
redirectUri: string
): Promise<BrowserSessionResult> {
console.log('[openAuthSession] called')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should the console.logs in this function be gated behind a debug flag or something?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are just for debugging/development. They will be removed before beta release

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(this is just a merge into the feature branch - to break up what otherwise would be a 300+ file PR 😅 )

getDefault (): Promise<Credential | null>;
setDefault (cred: Credential | null): Promise<void>;
get tokenStorage (): TokenStorage;
tokenStorage: TokenStorage;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this is needed so RN can install its own storage at import time, but that be limited to a one time operation? this allows mutability even after initialization, right

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. This is intentional. JavaScript doesn't have a compiler, therefore any changes to configurations and/or defaults will be performed at runtime

Comment on lines +46 to +47
console.log('here 3');
console.log(err, (err as Error)?.stack);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI some additional debugging logs

// Check if it's a cancellation or a real error
// ASWebAuthenticationSessionError code 1 = user cancelled
let nsError = error as NSError
if nsError.code == 1 {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should probably compare to ASWebAuthenticationSessionErrorCodeCanceledLogin instead of the underlying enum value 1.

// Set presentation context provider for iOS 13+
if #available(iOS 13.0, *) {
authSession.presentationContextProvider = self
authSession.prefersEphemeralWebBrowserSession = false
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should provide a mechanism for a developer to indicate that they want ephemeral sessions.

guard !completed else { return }
completed = true
reject("browser_session_error", "Failed to start authentication session", nil)
self.authSession = nil
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You set authSession to nil here, but when the callback is invoked above (either through an error or success response) we don't set it to nil. Depending on how this bridge plugin's lifecycle is managed by ReactNative it may leak.

Comment on lines +106 to +107
// Keep a reference to prevent deallocation
self.authSession = authSession
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're assigning the new authSession, but aren't checking to see if there's already one presented. If this value is not nil, we should cancel the previous one.

Comment on lines +8 to +21
override init() {
super.init()
print("✅ TokenStorageBridge initialized!")
// Debug: Print all methods
let methodCount = UnsafeMutablePointer<UInt32>.allocate(capacity: 1)
let methods = class_copyMethodList(type(of: self), methodCount)
print("📋 TokenStorageBridge methods:")
for i in 0..<Int(methodCount.pointee) {
if let method = methods?[i] {
print(" - \(NSStringFromSelector(method_getName(method)))")
}
}
free(methods)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation here is weird. Plus we probably don't want to include these debugging print statements long-term.

Comment on lines +11 to +12
name: "RNTokenStorageBridge",
targets: ["RNTokenStorageBridge"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like you only have a token storage bridge target, but aren't including the session bridge? Is this for a reason? Typically you'd probably just want to have a single library target for the overall collection of bridges

Comment on lines +18 to +21
name: "RNTokenStorageBridge",
path: "Sources/RNTokenStorageBridge",
exclude: ["TokenStorageBridge.swift", "TokenStorageBridge.m", "TokenStorageBridge.h"],
publicHeadersPath: "."
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This format is strange. You shouldn't need to specify the path here since it'll assume the subdirectory of the same name as the target within the Sources directory. Also is there a reason why you're excluding all the source files?

Comment on lines +26 to +27
path: "Tests/RNTokenStorageBridgeTests",
exclude: ["TokenStorageBridgeTests.swift"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can probably delete these lines since it'll automatically pick up all test sources within the proper directory within Tests. Also, it looks like you're skipping the test.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the .disabled file?

value: String,
accessibility: CFString
) throws {
let data = value.data(using: .utf8)!
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Force unwrapping this isn't a good idea; this SHOULD technically work, but if there's an edge-case or invalid data that is passed, this will crash the app. Instead we should ensure the value is correct, or throw an error

Suggested change
let data = value.data(using: .utf8)!
guard let data = value.data(using: .utf8) else {
throw SomeError.blah
}

Comment on lines +71 to +80
// Loop to delete all items matching the query (defensive against duplicates)
while true {
let status = SecItemDelete(query as CFDictionary)
if status == errSecItemNotFound {
break
} else if status != errSecSuccess {
throw NSError(domain: NSOSStatusErrorDomain, code: Int(status))
}
// If successful, loop again to ensure all items are deleted
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to loop to perform a deletion, since SecItemDelete will delete all items matching the query, as long as kSecMatchLimit isn't specified.

Comment on lines +114 to +124
// SecItemDelete only deletes one item per call, so loop until empty
while true {
let status = SecItemDelete(query as CFDictionary)
if status == errSecItemNotFound {
// No more items to delete
break
} else if status != errSecSuccess {
throw NSError(domain: NSOSStatusErrorDomain, code: Int(status))
}
// If successful, loop again to delete next item
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't need to loop either, as anything that matches the query will be deleted

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants