Skip to content

Add Login Spec #532

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 9, 2025
Merged

Add Login Spec #532

merged 8 commits into from
Apr 9, 2025

Conversation

jkmassel
Copy link
Contributor

@jkmassel jkmassel commented Feb 6, 2025

As discussed with @oguzkocer – there are a lot of different things that can happen during login, so let's document them so we can ensure we're testing them for each HTTP implementation (the native Rust one, Apple platforms' URLSession, and Android)

@jkmassel jkmassel force-pushed the add/login-scenarios branch 8 times, most recently from a87e570 to b351665 Compare February 12, 2025 02:38
@jkmassel jkmassel force-pushed the add/login-scenarios branch 4 times, most recently from 68c4b18 to bc249b1 Compare February 14, 2025 20:26
Copy link
Contributor

@oguzkocer oguzkocer left a comment

Choose a reason for hiding this comment

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

@jkmassel I've left some comments about the Rust implementation. I haven't reviewed the documentation or the Swift changes yet.


Reference Implementation: http://localhost

# 3: WordPress Admin URL Entry
Copy link
Contributor

Choose a reason for hiding this comment

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

This says WordPress Admin, but it handles both /wp-login and wp-admin cases, should we rename it?

@jkmassel jkmassel force-pushed the add/login-scenarios branch 2 times, most recently from cd36fa5 to b35616a Compare February 20, 2025 17:53
@jkmassel jkmassel force-pushed the add/login-scenarios branch from f394028 to 7e0c993 Compare February 20, 2025 20:59
@jkmassel jkmassel changed the base branch from trunk to move/login-errors-to-rust February 20, 2025 20:59
@jkmassel jkmassel force-pushed the add/login-scenarios branch 2 times, most recently from 4db0bf9 to c1983be Compare February 20, 2025 22:34
@jkmassel jkmassel force-pushed the move/login-errors-to-rust branch from 82ec5ab to 19ebcb2 Compare February 20, 2025 22:35
Base automatically changed from move/login-errors-to-rust to trunk February 20, 2025 23:02
@jkmassel jkmassel force-pushed the add/login-scenarios branch 5 times, most recently from 1de057f to 17c1c30 Compare February 21, 2025 18:35
@jkmassel jkmassel force-pushed the add/login-scenarios branch from 5c612dd to cf435e4 Compare March 17, 2025 21:03
@jkmassel jkmassel force-pushed the add/login-scenarios branch 6 times, most recently from 8343090 to 8ec93da Compare April 2, 2025 03:48
Copy link
Contributor

@crazytonyli crazytonyli left a comment

Choose a reason for hiding this comment

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

Regarding the Rust API comments, I tried to make the changes myself. But as I was making changes to them, it came to me that you guys may have tried that already. So, I haven't gone too deep into them, but I'm happy to make some proof of concept changes if needed.

@@ -17,3 +17,23 @@ public extension Date {
wordpressDateFormatter.date(from: string)
}
}

public extension TimeInterval {
static func fromRetryHeaderValue(_ value: String, now: Date = Date()) -> TimeInterval? {
Copy link
Contributor

Choose a reason for hiding this comment

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

This function does not seem to be used anywhere except unit tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed in 8e99aa7 – this was moved to Rust!


private func fetch(request: URLRequest) async throws -> (Data, URLResponse) {
if #available(macOS 12.0, *) {
return try await self.session.data(for: request, delegate: executorDelegate)
Copy link
Contributor

Choose a reason for hiding this comment

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

This function is available on all Apple platforms. Maybe we can change this function to be something like this?

#if os(Linux)
  return session.data(for: request)
#else
  return session.data(for: request, delegate: executorDelegate)
#endif

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 23fdee9 – this was because all the development happened on macOS

}

private func getPeerCertificateChain(_ error: Error) throws -> [SSLCertificateInfo] {
private func getPeerCertificateChain(_ error: Error) -> [SSLCertificateInfo]? {
let nserror = error as NSError
let info = nserror.userInfo

guard let certChainArray = info["NSErrorPeerCertificateChainKey"] as? NSArray else {
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't need the parseCertificateChain function below if changing this casting statement to info["NSErrorPeerCertificateChainKey"] as? [SecCertificate]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice! Done in c07115c

forSite proposedSiteUrl: String
) async throws(AutoDiscoveryAttemptFailure) -> AutoDiscoveryAttemptResult {

let discoveryResult = await client.apiDiscovery(siteUrl: proposedSiteUrl)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we change this function to fn api_discovery(&self, site_url: String) -> Result<Arc<AutoDiscoveryAttemptSuccess>, AutoDiscoveryAttemptFailure> and do the if-else checks below within Rust?

Copy link
Contributor

Choose a reason for hiding this comment

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

The function currently returns AutoDiscoveryResult/AutoDiscoveryUniffiResult which is the result of combination of multiple attempts, so this suggestion wouldn't work. Happy to elaborate on it if you need more details.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes. I'd love more details.

I haven't looked the how Kotlin uses the api_discovery function. But the Swift function here turns the AutoDiscoveryUniffiResult into either a success result or a failure, which essentially is Result<Arc<AutoDiscoveryAttemptSuccess>, AutoDiscoveryAttemptFailure>.


init(
urlSession: URLSession,
additionalHttpHeadersForAllRequests: [String: String] = [:]
Copy link
Contributor

Choose a reason for hiding this comment

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

This argument is not used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not by our code, but this is a good back-door way to add a user agent (for instance).

Should that just be a separate PR?

Copy link
Contributor

Choose a reason for hiding this comment

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

The same feature can be achieved using URLSession. But I guess this argument would allow adding headers only for API requests originated from the library. I'm okay with it being in this PR if it's needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm hoping we can limit the number of things that a developer has to handle themselves in URLSession, but I guess we'll see?

import FoundationNetworking
#endif

public final class WordPressLoginClient {
Copy link
Contributor

Choose a reason for hiding this comment

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

BTW, there is not much going on in this type. Can we use UniffiWpLoginClient directly? You'll need to add the loginURL(...) and credentials(...) helper functions to it, but I don't think that's a terrible thing to do.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd be fine if we get there eventually, but the name is wrong – nobody consuming this library should ever see UniffiWpLoginClient. At the very least we'll have to typealias it

Copy link
Contributor

@oguzkocer oguzkocer left a comment

Choose a reason for hiding this comment

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

I haven't finished the review yet, but I left some pretty important comments about the Kotlin implementation which may affect the remaining files I haven't reviewed yet.

P.S: I usually review my comments before posting, but I am not able to do that at the moment and don't want to risk losing them. So, if there is anything poorly worded or plain incorrect, I apologize in advance as it was unintentional.


tasks.register<Copy>("copySampleJSON") {
dependsOn(tasks.named("deleteTestResources"))
from("$cargoProjectRoot/native/swift/Tests/wordpress-api/Resources/Responses/localhost-json-root.json")
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably best to put this somewhere in the native directory, rather than under swift or kotlin.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd say let's do this as soon as we have a second item

@jkmassel jkmassel marked this pull request as ready for review April 3, 2025 18:52
func testNotWordPressSite() async throws {
let client = WordPressLoginClient(urlSession: .shared)
await #expect(performing: {
_ = try await client.findLoginUrl(forSite: "https://google.com")
Copy link
Contributor

Choose a reason for hiding this comment

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

When the input is "google.com", the returned error is ParseUrlError.RelativeUrlWithoutBase. IMO that's an incorrect error. I'd expect the error to be probablyNotAWordPressSite.

Copy link
Contributor

Choose a reason for hiding this comment

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

I tested this in Rust with the new combinedResult function from #652 and it returned ProbablyNotAWordPressSite as expected. Once this PR is updated, the issue should be resolved for Swift as well.

// Full error message for `google.com`
FindApiRoot {
    parsed_site_url: ParsedUrl {
        inner: Url {
            scheme: "https",
            cannot_be_a_base: false,
            username: "",
            password: None,
            host: Some(
                Domain(
                    "google.com",
                ),
            ),
            port: None,
            path: "/",
            query: None,
            fragment: None,
        },
    },
    find_api_root_failure: ProbablyNotAWordPressSite,
}

Copy link
Contributor

@crazytonyli crazytonyli Apr 9, 2025

Choose a reason for hiding this comment

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

@jkmassel Do you mind adding google.com as a test case, which should be fairly easy with @Test(arguments: ["google.com", "https://google.com"])?

@jkmassel jkmassel force-pushed the add/login-scenarios branch from 8ec93da to e8a1e3e Compare April 9, 2025 01:49
Copy link
Contributor

@crazytonyli crazytonyli left a comment

Choose a reason for hiding this comment

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

Left a couple of minor comments.


public protocol AuthenticatorProtocol {
func authenticate(url: URL, callbackURL: URL) async throws -> URL
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This protocol can be removed.

@jkmassel jkmassel enabled auto-merge (squash) April 9, 2025 02:36
@jkmassel jkmassel dismissed oguzkocer’s stale review April 9, 2025 02:42

We moved pretty much all the Kotlin stuff to #655

@jkmassel jkmassel merged commit fae32d5 into trunk Apr 9, 2025
20 checks passed
@jkmassel jkmassel deleted the add/login-scenarios branch April 9, 2025 03:39
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