diff --git a/docs/src/content/docs/oauth/hubspot.mdx b/docs/src/content/docs/oauth/hubspot.mdx
new file mode 100644
index 0000000..7176daf
--- /dev/null
+++ b/docs/src/content/docs/oauth/hubspot.mdx
@@ -0,0 +1,223 @@
+---
+title: HubSpot Authorization Provider
+description: Add HubSpot authorization provider to Aura Auth to authentication and authorize
+---
+
+## HubSpot
+
+Set up the `HubSpot` 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 `HubSpot` provider for Aura Auth.
+
+- [HubSpot OAuth App](#hubspot-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 HubSpot (Client & Server)](#sign-in-to-hubspot-client--server)
+- [Resources](#resources)
+
+---
+
+
+
+
+
+## HubSpot OAuth App
+
+### Register the Application
+
+The first step is to create and register an OAuth App on the HubSpot Developers Portal to obtain access to the user's resources.
+
+1. Navigate to your HubSpot left sidebar and go to **Development > Legacy Apps**.
+2. Click **Create legacy app**.
+3. Fill the App info including the **Public app name** and **Description**.
+4. Click the **Auth** tab and fill:
+ - Set the **Redirect URLs** to `http://localhost:3000/auth/callback/hubspot`.
+ - _(Make sure to replace `localhost:3000` with your production domain when deploying)_
+5. Click **Create app**.
+6. Ensure you copy the **Client ID** and click **Generate a new client secret**.
+
+
+
+
+
+## Installation
+
+Install the package using a package manager like `npm`, `pnpm`, or `yarn`:
+
+```npm
+npm install @aura-stack/auth
+```
+
+
+
+
+
+## Environment setup
+
+Now, you must configure the environment variables required by Aura Auth, including the HubSpot 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"
+
+# HubSpot Credentials
+AURA_AUTH_HUBSPOT_CLIENT_ID="your_hubspot_client_id"
+AURA_AUTH_HUBSPOT_CLIENT_SECRET="your_hubspot_client_secret"
+```
+
+
+ **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.
+
+
+
+
+
+
+## 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: ["hubspot"],
+})
+
+// Extract the required utilities
+export const { handlers, api, jose } = auth
+```
+
+
+ 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.).
+
+
+
+
+
+
+## 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 { hubspot } from "@aura-stack/auth/oauth/hubspot"
+
+export const auth = createAuth({
+ oauth: [
+ hubspot({
+ authorize: {
+ params: {
+ // Override default scopes
+ scope: "read:user user:email",
+ },
+ },
+ }),
+ ],
+})
+
+export const { handlers, api, jose } = auth
+```
+
+
+
+
+
+## Sign In to HubSpot (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/hubspot`
+
+---
+
+### Client-Side (React, Vue, etc.)
+
+You can utilize the `createAuthClient` utility to programmatically trigger sign-ins. You can also define a `redirectTo` destination.
+
+
+ **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.
+
+
+```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("hubspot", {
+ 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("hubspot", {
+ redirectTo: "http://localhost:3000/dashboard",
+ })
+
+ // Example returning redirect location
+ return response.headers.get("Location")
+}
+```
+
+---
+
+### 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 HubSpot 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
+```
+
+
+
+
+
+---
+
+## Resources
+
+- [RFC - The OAuth 2.0 Authorization Framework](https://datatracker.ietf.org/doc/html/rfc6749)
+- [HubSpot - Working with OAuth](https://developers.hubspot.com/docs/apps/legacy-apps/authentication/oauth-quickstart-guide#getting-oauth-tokens)
+- [HubSpot - Scopes](https://developers.hubspot.com/docs/apps/legacy-apps/authentication/scopes)
+- [HubSpot - Retrieve OAuth token metadata](https://developers.hubspot.com/docs/api-reference/legacy/authentication/oauth-tokens/v1/get-oauth-token-metadata)
diff --git a/packages/core/src/oauth/dribble.ts b/packages/core/src/oauth/dribbble.ts
similarity index 100%
rename from packages/core/src/oauth/dribble.ts
rename to packages/core/src/oauth/dribbble.ts
diff --git a/packages/core/src/oauth/hubspot.ts b/packages/core/src/oauth/hubspot.ts
new file mode 100644
index 0000000..27bad2d
--- /dev/null
+++ b/packages/core/src/oauth/hubspot.ts
@@ -0,0 +1,87 @@
+import type { OAuthProviderConfig, User } from "@/@types/index.ts"
+
+/**
+ * @see [HubSpot - Retrieve OAuth token metadata](https://developers.hubspot.com/docs/api-reference/legacy/authentication/oauth-tokens/v1/get-oauth-token-metadata)
+ */
+export interface HubSpotProfile {
+ /**
+ * The ID of the application associated with the access token.
+ */
+ app_id: number
+ /**
+ * The time in seconds until the access token expires.
+ */
+ expires_in: number
+ /**
+ * The ID of the HubSpot account associated with the access token.
+ */
+ hub_id: number
+ /**
+ * An array of strings indicating the scopes
+ */
+ scopes: string[]
+ /**
+ * The access token string used to make API calls.
+ */
+ token: string
+ /**
+ * The type of token, typically indicating the authentication scheme.
+ * @default `bearer`
+ */
+ token_type: string
+ /**
+ * The ID of the hubspot user for whom the access token was created.
+ */
+ user_id: number
+ /**
+ * The domain of the HubSpot account associated with the access token.
+ */
+ hub_domain: string
+ /**
+ * Indicates whether the token is for a privately distributed application. If false, it is marketplace distributed.
+ */
+ is_private_distribution: boolean
+ /**
+ * The email address of the hubspot user for whom the access token was created.
+ */
+ user: string
+}
+
+/**
+ * HubSpot OAuth provider
+ * Profile Type {@link HubSpotProfile}
+ *
+ * @see [HubSpot - Working with OAuth](https://developers.hubspot.com/docs/apps/legacy-apps/authentication/oauth-quickstart-guide#getting-oauth-tokens)
+ * @see [HubSpot - Scopes](https://developers.hubspot.com/docs/apps/legacy-apps/authentication/scopes)
+ * @see [HubSpot - Retrieve OAuth token metadata](https://developers.hubspot.com/docs/api-reference/legacy/authentication/oauth-tokens/v1/get-oauth-token-metadata)
+ */
+export const hubspot = (
+ options?: OAuthProviderConfig
+): OAuthProviderConfig => {
+ return {
+ id: "hubspot",
+ name: "HubSpot",
+ authorize: {
+ url: "https://app.hubspot.com/oauth/authorize",
+ params: {
+ scope: "oauth",
+ },
+ },
+ accessToken: "https://api.hubapi.com/oauth/v1/token",
+ /**
+ * @todo: HubSpot doesn't have a dedicated userinfo endpoint, but we can retrieve
+ * the user information from the access token metadata endpoint.
+ * Expected: `https://api.hubapi.com/oauth/v1/access-tokens/{token}`
+ */
+ userInfo: "https://api.hubapi.com/oauth/v1/access-tokens",
+ profile: (profile) => {
+ return {
+ sub: String(profile.user_id),
+ name: profile.user,
+ email: null,
+ image: null,
+ } as DefaultUser
+ },
+ ...options,
+ }
+}
diff --git a/packages/core/src/oauth/index.ts b/packages/core/src/oauth/index.ts
index a33c799..1100d14 100644
--- a/packages/core/src/oauth/index.ts
+++ b/packages/core/src/oauth/index.ts
@@ -20,7 +20,8 @@ import { notion } from "./notion.ts"
import { dropbox } from "./dropbox.ts"
import { atlassian } from "./atlassian.ts"
import { clickUp } from "./click-up.ts"
-import { dribble } from "./dribble.ts"
+import { dribbble } from "./dribbble.ts"
+import { hubspot } from "./hubspot.ts"
import { formatZodError } from "@/shared/utils.ts"
import { AuthInternalError } from "@/shared/errors.ts"
import { OAuthEnvSchema, OAuthProviderCredentialsSchema } from "@/schemas.ts"
@@ -40,7 +41,8 @@ export * from "./notion.ts"
export * from "./dropbox.ts"
export * from "./atlassian.ts"
export * from "./click-up.ts"
-export * from "./dribble.ts"
+export * from "./dribbble.ts"
+export * from "./hubspot.ts"
export const builtInOAuthProviders = {
github,
@@ -58,7 +60,8 @@ export const builtInOAuthProviders = {
dropbox,
atlassian,
clickUp,
- dribble,
+ dribbble,
+ hubspot,
} as const
/**