diff --git a/CHANGELOG.md b/CHANGELOG.md index ded84b2..4774dad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ as the release notes. ## [Unreleased] +### Changed + +- Signing in with an app password needs only the handle: Indigo finds the account's PDS from it, so the Service field is not needed. An empty Service starts from https://bsky.social. The Service field is still used for the browser (OAuth node) sign-in. This needs Wolfram v0.38.0's `wf_agent_login_discovered`, which is not released yet, so this PR is blocked on that release. ([#72](https://github.com/ewanc26/indigo/pull/72)) + ### Fixed - The timeline header's button hints are drawn smaller, so "START Exit" no longer runs into the post counter on the top screen (seen in Azahar). The layout's character-width estimate is too narrow for the system font; this is the minimum change, not a font change. ([#71](https://github.com/ewanc26/indigo/pull/71)) diff --git a/src/app/app.c b/src/app/app.c index c191c02..eb0e10b 100644 --- a/src/app/app.c +++ b/src/app/app.c @@ -46,7 +46,8 @@ try_sign_in(indigo_app *app) return; } if (!indigo_signin_ready(s)) { - indigo_app_set_status(s, "Fill in the OAuth node URL and handle first.", true); + indigo_app_set_status(s, s->handle[0] ? "Fill in the OAuth node URL first." + : "Fill in the handle first.", true); return; } app->request = INDIGO_REQUEST_SIGN_IN; diff --git a/src/app/signin.c b/src/app/signin.c index 7f42cca..bbd1c3b 100644 --- a/src/app/signin.c +++ b/src/app/signin.c @@ -203,8 +203,13 @@ indigo_signin_field_label(indigo_field f) bool indigo_signin_ready(const indigo_signin *s) { - /* An empty password selects the browser-based OAuth flow. */ - return s->service[0] && s->handle[0]; + /* A password sign-in needs only the handle: the PDS is discovered from it, + * and an empty Service starts from INDIGO_DEFAULT_SERVICE. An empty + * password selects the browser-based OAuth flow, which needs the node. */ + if (!s->handle[0]) { + return false; + } + return s->password[0] ? true : s->service[0] != '\0'; } void diff --git a/src/atproto/session_auth.c b/src/atproto/session_auth.c index dea1f8b..6fe76d6 100644 --- a/src/atproto/session_auth.c +++ b/src/atproto/session_auth.c @@ -2,6 +2,8 @@ #include "atproto/session_internal.h" +#include "app/app.h" + #if defined(__3DS__) && defined(WOLFRAM_3DS) void @@ -192,6 +194,7 @@ indigo_session_do_login(const indigo_job *j) wf_status st; g_session.node_session = false; const char *who; + char *pds = NULL; indigo_session_drop_agent(); g_session.agent = new_agent(j->service); @@ -200,19 +203,25 @@ indigo_session_do_login(const indigo_job *j) return; } + /* The handle or DID resolves to the account's PDS, which the agent is + * pointed at before the login is sent. The user never types that host. */ indigo_log_info("sign-in: contacting %s", j->service); - st = wf_agent_login(g_session.agent, j->identifier, j->password); + st = wf_agent_login_discovered(g_session.agent, j->identifier, j->password, &pds); if (st != WF_OK) { wf_failure_kind f = wf_failure_classify(st, 0, NULL); indigo_log_warn("sign-in failed: wolfram status %d (%s)", (int) st, wf_failure_tag(f)); + free(pds); indigo_session_drop_agent(); indigo_session_publish(INDIGO_SESSION_EVENT_SIGN_IN_FAILED, f, NULL); return; } - remember(j->service); + /* The saved session keeps the discovered PDS, so a resume goes straight + * there. Fall back to the starting host only if none was reported. */ + remember(pds ? pds : j->service); + free(pds); who = wf_agent_get_handle(g_session.agent); indigo_log_info("signed in as %s", who ? who : "(unknown)"); indigo_session_publish(INDIGO_SESSION_EVENT_SIGNED_IN, WF_FAIL_NONE, who); @@ -331,7 +340,10 @@ indigo_session_submit_login(const char *service, const char *identifier, indigo_job j = {.kind = JOB_LOGIN}; bool ok; - snprintf(j.service, sizeof j.service, "%s", service); + /* The password path starts from the default host when no Service is set; + * the login then moves to the account's own PDS. */ + snprintf(j.service, sizeof j.service, "%s", + service && service[0] ? service : INDIGO_DEFAULT_SERVICE); snprintf(j.identifier, sizeof j.identifier, "%s", identifier); snprintf(j.password, sizeof j.password, "%s", password); ok = indigo_session_enqueue(&j); diff --git a/tests/tests.c b/tests/tests.c index 9817da4..c4b85db 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -878,6 +878,29 @@ test_signin_fields(void) CHECK(indigo_signin_set_field(&s, INDIGO_FIELD_PASSWORD, "") == INDIGO_INPUT_EMPTY); CHECK(strcmp(s.password, "abcd-efgh-ijkl-mnop") == 0); + /* A password sign-in is ready from the handle alone: the PDS is + * discovered, so no Service is needed. An empty handle never is. */ + indigo_signin_init(&s); + CHECK(indigo_signin_set_field(&s, INDIGO_FIELD_HANDLE, "ewancroft.uk") == INDIGO_INPUT_OK); + CHECK(indigo_signin_set_field(&s, INDIGO_FIELD_PASSWORD, "abcd-efgh-ijkl-mnop") == INDIGO_INPUT_OK); + s.service[0] = '\0'; + CHECK(indigo_signin_ready(&s)); + s.handle[0] = '\0'; + CHECK(!indigo_signin_ready(&s)); + indigo_signin_init(&s); + CHECK(indigo_signin_set_field(&s, INDIGO_FIELD_HANDLE, "ewancroft.uk") == INDIGO_INPUT_OK); + CHECK(indigo_signin_ready(&s)); + /* Without a password the browser (OAuth node) sign-in needs a Service. */ + s.service[0] = '\0'; + CHECK(!indigo_signin_ready(&s)); + indigo_signin_init(&s); + CHECK(indigo_signin_set_field(&s, INDIGO_FIELD_PASSWORD, "abcd-efgh-ijkl-mnop") == INDIGO_INPUT_OK); + CHECK(!indigo_signin_ready(&s)); + + indigo_signin_init(&s); + CHECK(indigo_signin_set_field(&s, INDIGO_FIELD_HANDLE, "@ewancroft.uk") == INDIGO_INPUT_OK); + CHECK(indigo_signin_set_field(&s, INDIGO_FIELD_PASSWORD, "abcd-efgh-ijkl-mnop\n") == INDIGO_INPUT_OK); + /* The password is never shown, only its length. */ indigo_signin_display(&s, INDIGO_FIELD_PASSWORD, shown, sizeof shown); CHECK(strlen(shown) == strlen("abcd-efgh-ijkl-mnop")); diff --git a/wolfram.ref b/wolfram.ref index 945a6da..765098d 100644 --- a/wolfram.ref +++ b/wolfram.ref @@ -1 +1 @@ -v0.37.0 +v0.38.0