-
Notifications
You must be signed in to change notification settings - Fork 1
feat(oauth): add Coinbase OAuth provider
#152
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,224 @@ | ||
| --- | ||
| title: Coinbase Authorization Provider | ||
| description: Add Coinbase authorization provider to Aura Auth to authentication and authorize | ||
| --- | ||
|
|
||
| ## Coinbase | ||
|
|
||
| Set up the `Coinbase` authorization provider in your Aura Auth instance. | ||
|
|
||
| --- | ||
|
|
||
| ## What you'll build | ||
|
|
||
| Through this quick start guide you are going to learn and understand the basics and how to set up the `Coinbase` provider for Aura Auth. | ||
|
|
||
| - [Coinbase OAuth App](#coinbase-oauth-app) | ||
| - [Installation](#installation) | ||
| - [Environment setup](#environment-setup) | ||
| - [Configure the Auth Instance](#configure-the-auth-instance) | ||
| - [Customizing the OAuth Provider](#customizing-the-oauth-provider) | ||
| - [Sign In to Coinbase (Client & Server)](#sign-in-to-coinbase-client--server) | ||
| - [Resources](#resources) | ||
|
|
||
| --- | ||
|
|
||
| <Steps> | ||
|
|
||
| <Step> | ||
|
|
||
| ## Coinbase OAuth App | ||
|
|
||
| ### Register the Application | ||
|
|
||
| The first step is to create and register an OAuth App on the Coinbase CDP Portal to obtain access to the user's resources. | ||
|
|
||
| 1. Navigate to your Coinbase profile and go to **API Keys > Secret API Keys**. | ||
| 2. Click **Create API Key**. | ||
| 3. Fill in the "API Key nickname". | ||
| 4. Register the API restrictions: | ||
| - IP allowlist: `localhost:3000` (or your production domain when deploying) | ||
|
|
||
| 5. Set the **Authorization callback URL** to `http://localhost:3000/auth/callback/coinbase`. | ||
| - _(Make sure to replace `localhost:3000` with your production domain when deploying)_ | ||
| 6. Click **Register application**. | ||
| 7. Ensure you copy the **Client ID** and click **Generate a new client secret**. | ||
|
|
||
| </Step> | ||
|
|
||
| <Step> | ||
|
|
||
| ## Installation | ||
|
|
||
| Install the package using a package manager like `npm`, `pnpm`, or `yarn`: | ||
|
|
||
| ```npm | ||
| npm install @aura-stack/auth | ||
| ``` | ||
|
|
||
| </Step> | ||
|
|
||
| <Step> | ||
|
|
||
| ## Environment setup | ||
|
|
||
| Now, you must configure the environment variables required by Aura Auth, including the Coinbase credentials and the encryption secrets. | ||
|
|
||
| ```bash title=".env" lineNumbers | ||
| # Aura Secrets | ||
| AURA_AUTH_SECRET="your-32-byte-secret" | ||
| AURA_AUTH_SALT="your-32-byte-salt" | ||
|
|
||
| # Coinbase Credentials | ||
| AURA_AUTH_COINBASE_CLIENT_ID="your_coinbase_client_id" | ||
| AURA_AUTH_COINBASE_CLIENT_SECRET="your_coinbase_client_secret" | ||
| ``` | ||
|
|
||
| <Callout type="warn"> | ||
| **CRITICAL SECURITY WARNING:** The `AURA_AUTH_SECRET` and `AURA_AUTH_SALT` variables are used to encrypt and sign user sessions. | ||
| These MUST be securely generated, highly randomized strings consisting of at least 32 bytes to ensure adequate entropy. Never | ||
| hardcode these values in your repository. Use a secure generator (like `openssl rand -base64 32`) to create them, and store them | ||
| exclusively in your secure environment variables manager. | ||
| </Callout> | ||
|
|
||
| </Step> | ||
|
|
||
| <Step> | ||
|
|
||
| ## Configure the Auth Instance | ||
|
|
||
| Configure the `createAuth` instance inside an `auth.ts` file located at the root of your project. Ensure you explicitly export the `handlers`, `api`, and `jose` objects. | ||
|
|
||
| ```ts title="auth.ts" lineNumbers | ||
| import { createAuth } from "@aura-stack/auth" | ||
|
|
||
| export const auth = createAuth({ | ||
| oauth: ["coinbase"], | ||
| }) | ||
|
|
||
| // Extract the required utilities | ||
| export const { handlers, api, jose } = auth | ||
| ``` | ||
|
|
||
| <Callout type="info"> | ||
| The `handlers` object contains mapping utilities for standard HTTP methods (`GET`, `POST`, `PATCH`) as well as a unified `ALL` | ||
| handler. This allows you to easily mount the authentication routes across any framework (Next.js, Elysia, Express, etc.). | ||
| </Callout> | ||
|
|
||
| </Step> | ||
|
|
||
| <Step> | ||
|
|
||
| ## Customizing the OAuth Provider | ||
|
|
||
| If you need to define custom scopes, change the response type, or map profile data differently, you can use the provider's factory function instead of a simple string identifier. | ||
|
|
||
| ```ts title="auth.ts" lineNumbers | ||
| import { createAuth } from "@aura-stack/auth" | ||
| import { coinbase } from "@aura-stack/auth/oauth/coinbase" | ||
|
|
||
| export const auth = createAuth({ | ||
| oauth: [ | ||
| coinbase({ | ||
| authorize: { | ||
| params: { | ||
| // Override default scopes | ||
| scope: "wallet:user:read wallet:user:email", | ||
| }, | ||
| }, | ||
| }), | ||
| ], | ||
| }) | ||
|
halvaradop marked this conversation as resolved.
|
||
|
|
||
| export const { handlers, api, jose } = auth | ||
| ``` | ||
|
|
||
| </Step> | ||
|
|
||
| <Step> | ||
|
|
||
| ## Sign In to Coinbase (Client & Server) | ||
|
|
||
| There are multiple ways to trigger the sign-in flow depending on your ecosystem. | ||
|
|
||
| ### Sign-in Path (Direct Navigation) | ||
|
|
||
| The common route to trigger the auth flow natively without needing a client library is simply navigating the browser to: | ||
| `http://localhost:3000/auth/signIn/coinbase` | ||
|
|
||
| --- | ||
|
|
||
| ### Client-Side (React, Vue, etc.) | ||
|
|
||
| You can utilize the `createAuthClient` utility to programmatically trigger sign-ins. You can also define a `redirectTo` destination. | ||
|
|
||
| <Callout type="warn"> | ||
| **Constraint Rule**: The `baseURL` passed into `createAuthClient` MUST exactly match the root domain and path where the HTTP | ||
| `handlers` expose their endpoints on the server. | ||
| </Callout> | ||
|
|
||
| ```ts title="components/Login.tsx" lineNumbers | ||
| import { createAuthClient } from "@aura-stack/auth/client" | ||
|
|
||
| export const authClient = createAuthClient({ | ||
| baseURL: "http://localhost:3000/auth", | ||
| }) | ||
|
|
||
| const triggerSignIn = async () => { | ||
| await authClient.signIn("coinbase", { | ||
| redirectTo: "/dashboard", | ||
| }) | ||
| } | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ### Server-Side (Next.js Actions, Remix Loaders, etc.) | ||
|
|
||
| For environments supporting server-side actions, use the programmatic `api.signIn` method securely. | ||
|
|
||
| ```ts title="actions.ts" lineNumbers | ||
| import { api } from "./auth" | ||
|
|
||
| export const serverSignIn = async () => { | ||
| const response = await api.signIn("coinbase", { | ||
| redirectTo: "http://localhost:3000/dashboard", | ||
| }) | ||
|
|
||
| // Example returning redirect location | ||
| return response.headers.get("Location") | ||
| } | ||
| ``` | ||
|
halvaradop marked this conversation as resolved.
|
||
|
|
||
| --- | ||
|
|
||
| ### Session Retrieval | ||
|
|
||
| After a user successfully signs in, you can retrieve their session data securely. | ||
|
|
||
| **Client-Side:** | ||
|
|
||
| ```ts | ||
| const session = await authClient.getSession() | ||
| console.log(session?.user) // The authenticated Coinbase user profile | ||
| ``` | ||
|
|
||
| **Server-Side:** | ||
|
|
||
| ```ts | ||
| // Note: You must pass the native Web Request object or Headers! | ||
| const session = await api.getSession(request) | ||
| console.log(session?.user) // Safely retrieved backend session | ||
| ``` | ||
|
|
||
| </Step> | ||
|
|
||
| </Steps> | ||
|
|
||
| --- | ||
|
|
||
| ## Resources | ||
|
|
||
| - [RFC - The OAuth 2.0 Authorization Framework](https://datatracker.ietf.org/doc/html/rfc6749) | ||
| - [Coinbase - App OAuth2 Integration](https://docs.cdp.coinbase.com/coinbase-app/oauth2-integration/integrations) | ||
| - [Coinbase - Scopes](https://docs.cdp.coinbase.com/coinbase-app/oauth2-integration/scopes) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| import type { OAuthProviderCredentials, User } from "@/@types/index.ts" | ||
|
|
||
| export interface CoinbaseProfile { | ||
| data: { | ||
| id: string | ||
| name: string | ||
| username: string | ||
| profile_bio: string | null | ||
| profile_location: string | null | ||
| profile_url: string | ||
| avatar_url: string | ||
| resource: string | ||
| resource_path: string | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Coinbase OAuth Provider | ||
| * | ||
| * @see [Coinbase - App OAuth2 Integration](https://docs.cdp.coinbase.com/coinbase-app/oauth2-integration/integrations) | ||
| * @see [Coinbase - Scopes](https://docs.cdp.coinbase.com/coinbase-app/oauth2-integration/scopes) | ||
| */ | ||
| export const coinbase = <DefaultUser extends User = User>( | ||
| options?: Partial<OAuthProviderCredentials<CoinbaseProfile, DefaultUser>> | ||
| ): OAuthProviderCredentials<CoinbaseProfile, DefaultUser> => { | ||
| return { | ||
| id: "coinbase", | ||
| name: "Coinbase", | ||
| authorize: { | ||
| url: "https://login.coinbase.com/oauth2/auth", | ||
| params: { | ||
| scope: "wallet:user:read+wallet:user:email", | ||
|
halvaradop marked this conversation as resolved.
|
||
| responseType: "code", | ||
| }, | ||
| }, | ||
| accessToken: "https://login.coinbase.com/oauth2/token", | ||
| userInfo: "https://api.coinbase.com/v2/user", | ||
| profile: (profile) => { | ||
| return { | ||
| sub: String(profile.data.id), | ||
| name: profile.data.name, | ||
| image: profile.data.avatar_url, | ||
| email: null, | ||
| } as DefaultUser | ||
| }, | ||
|
halvaradop marked this conversation as resolved.
|
||
| ...options, | ||
| } | ||
|
Comment on lines
+26
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# How do existing providers handle partial option merging?
fd -e ts . packages/core/src/oauth --exec rg -nH -C2 '\.\.\.options' {}Repository: aura-stack-ts/auth Length of output: 4630 🏁 Script executed: cat packages/core/src/oauth/coinbase.tsRepository: aura-stack-ts/auth Length of output: 1642 🏁 Script executed: head -150 docs/src/content/docs/oauth/coinbase.mdx | tail -30Repository: aura-stack-ts/auth Length of output: 641 🏁 Script executed: rg -nH 'OAuthProviderCredentials' packages/core/src/@types --max-count=5Repository: aura-stack-ts/auth Length of output: 714 🏁 Script executed: cat packages/core/src/@types/index.ts | head -100Repository: aura-stack-ts/auth Length of output: 1932 🏁 Script executed: cat packages/core/src/@types/oauth.tsRepository: aura-stack-ts/auth Length of output: 2800 🏁 Script executed: fd -e test -e spec . packages/core --type f | head -20Repository: aura-stack-ts/auth Length of output: 44 🏁 Script executed: rg -l "coinbase" --type ts packages/ | grep -i testRepository: aura-stack-ts/auth Length of output: 44 🏁 Script executed: cat packages/core/src/oauth/discord.tsRepository: aura-stack-ts/auth Length of output: 3591 🏁 Script executed: cat packages/core/src/oauth/github.tsRepository: aura-stack-ts/auth Length of output: 2860 This is a systemic issue affecting all OAuth providers, not just Coinbase. The shallow spread pattern with The fix should apply uniformly across all providers—either deep-merge 🤖 Prompt for AI Agents |
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Grammar fix in frontmatter
description.🔧 Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents