diff --git a/grid_api/routers/AGENTS.md b/grid_api/routers/AGENTS.md index 58bd2a3a..c3cd77ae 100644 --- a/grid_api/routers/AGENTS.md +++ b/grid_api/routers/AGENTS.md @@ -47,7 +47,9 @@ transport, accounts, stats, health/metrics. wallet, then verified email only when it is the sole identity); supplemental or unverified email must never join accounts. Native service/app exchange lives at `/v1/auth/service/exchange`; Google ID - tokens are verified at `/v1/auth/google/exchange`; partner wallet proof uses + tokens are verified at `/v1/auth/google/exchange`, which also returns the + canonical account's primary verified wallet when one is linked; partner + wallet proof uses `/v1/auth/wallet/challenge` plus `/v1/auth/wallet/exchange`, bound to the service, its exact `siwe_domains`, the app subject, wallet, URI, Base chain, expiry, and one-use nonce. `/v1/auth/service/bind` binds an app subject after diff --git a/grid_api/routers/accounts.py b/grid_api/routers/accounts.py index b745f3a8..4f4c9ceb 100644 --- a/grid_api/routers/accounts.py +++ b/grid_api/routers/accounts.py @@ -943,7 +943,14 @@ async def exchange_google_identity( account_id=account_id, ref=service_auth.new_event_ref("exchange", service["service_id"]), ) - return {"access_token": token, "token_type": "Bearer", "expires_in": 900, "account_id": str(account_id)} + wallets = await identities_svc.verified_wallet_addresses(account_id) + return { + "access_token": token, + "token_type": "Bearer", + "expires_in": 900, + "account_id": str(account_id), + "wallet": wallets[0] if wallets else None, + } @router.post("/v1/auth/service/bind") diff --git a/grid_api/services/tests/test_native_service_auth.py b/grid_api/services/tests/test_native_service_auth.py index b0d3aa05..ace5dfbc 100644 --- a/grid_api/services/tests/test_native_service_auth.py +++ b/grid_api/services/tests/test_native_service_auth.py @@ -362,6 +362,18 @@ async def no_value(*_args, **_kwargs): ) == canonical_id assert await credits.get_balance(UUID(canonical_id)) == 20_000 + repeated_google = await accounts_router.exchange_google_identity( + request, + accounts_router.GoogleExchangeForm( + id_token="google-proof-after-wallet-link", + app_subject=app_subject, + ), + apikey=key, + authorization=None, + ) + assert repeated_google["account_id"] == canonical_id + assert repeated_google["wallet"] == wallet.address.lower() + @pytest.mark.asyncio async def test_verified_wallet_account_and_balance_are_shared_across_products(