npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

better-auth-strava

v0.1.0

Published

Strava OAuth provider for better-auth

Readme

Better Auth + Strava

Strava OAuth provider for Better Auth with automatic token refresh and seamless session management.

Features

  • 🔐 OAuth 2.0 authentication with Strava
  • 🔄 Automatic token refresh (offline access)
  • 📧 Smart email handling (placeholder generation for Strava's no-email limitation)
  • 🎯 Type-safe session management
  • ⚡ Zero-config setup with Better Auth

Installation

npm install better-auth better-auth-strava
# or
bun add better-auth better-auth-strava

Quick Start

1. Get Strava API Credentials

  1. Go to Strava API Settings
  2. Create a new application
  3. Set your Authorization Callback Domain (e.g., localhost for development)
  4. Copy your Client ID and Client Secret

2. Configure Better Auth

// lib/auth.ts
import { betterAuth } from "better-auth";
import { strava } from "better-auth-strava";

export const auth = betterAuth({
  database: {
    provider: "postgres",
    url: process.env.DATABASE_URL!,
  },
  socialProviders: {
    strava: {
      clientId: process.env.STRAVA_CLIENT_ID!,
      clientSecret: process.env.STRAVA_CLIENT_SECRET!,
    },
  },
});

3. Create Auth Client

// lib/auth-client.ts
import { createAuthClient } from "better-auth/react";

export const authClient = createAuthClient({
  baseURL: process.env.NEXT_PUBLIC_AUTH_URL,
});

4. Add API Route Handler

Next.js App Router:

// app/api/auth/[...all]/route.ts
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";

export const { GET, POST } = toNextJsHandler(auth);

Next.js Pages Router:

// pages/api/auth/[...all].ts
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";

export default toNextJsHandler(auth);

5. Use in Your App

"use client";

import { authClient } from "@/lib/auth-client";

export default function LoginButton() {
  const { data: session, isPending } = authClient.useSession();

  const handleSignIn = async () => {
    await authClient.signIn.social({
      provider: "strava",
      callbackURL: "/dashboard",
    });
  };

  const handleSignOut = async () => {
    await authClient.signOut();
  };

  if (isPending) {
    return <div>Loading...</div>;
  }

  if (session) {
    return (
      <div>
        <p>Welcome, {session.user.name}!</p>
        <p>Athlete ID: {session.user.image}</p>
        <button onClick={handleSignOut}>Sign Out</button>
      </div>
    );
  }

  return <button onClick={handleSignIn}>Connect with Strava</button>;
}

Usage Examples

Access Strava Athlete Data

const { data: session } = authClient.useSession();

if (session) {
  console.log(session.user.name); // "John Doe"
  console.log(session.user.image); // Profile photo URL
  console.log(session.user.email); // "[email protected]" (placeholder)
}

Check for Placeholder Email

Strava doesn't provide email addresses. This package automatically generates placeholder emails:

const { data: session } = authClient.useSession();

if (session?.user.email.endsWith("@strava.local")) {
  // Prompt user to provide a real email
  console.log("Please add your email address");
}

Customize Scopes

import { betterAuth } from "better-auth";
import { strava } from "better-auth-strava";

export const auth = betterAuth({
  database: {
    provider: "postgres",
    url: process.env.DATABASE_URL!,
  },
  socialProviders: {
    strava: {
      clientId: process.env.STRAVA_CLIENT_ID!,
      clientSecret: process.env.STRAVA_CLIENT_SECRET!,
      scopes: ["read", "profile:read_all", "activity:read", "activity:write"],
    },
  },
});

Available Scopes

| Scope | Description | | ------------------- | ------------------------------------------ | | read | Read public profile data | | profile:read_all | Read all profile information | | activity:read | Read activity data | | activity:read_all | Read all activity data (including private) | | activity:write | Create and modify activities |

Server-Side Session Access

// app/api/user/route.ts
import { auth } from "@/lib/auth";
import { headers } from "next/headers";

export async function GET() {
  const session = await auth.api.getSession({
    headers: await headers(),
  });

  if (!session) {
    return Response.json({ error: "Unauthorized" }, { status: 401 });
  }

  return Response.json({
    user: session.user,
  });
}

Protect Routes with Middleware

// middleware.ts
import { auth } from "@/lib/auth";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export async function middleware(request: NextRequest) {
  const session = await auth.api.getSession({
    headers: request.headers,
  });

  if (!session && request.nextUrl.pathname.startsWith("/dashboard")) {
    return NextResponse.redirect(new URL("/", request.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: ["/dashboard/:path*"],
};

Configuration Options

Strava Provider Options

strava({
  clientId: string;              // Required: Your Strava OAuth client ID
  clientSecret: string;          // Required: Your Strava OAuth client secret
  redirectURI?: string;          // Optional: Override redirect URI
  scopes?: string[];            // Optional: Custom scopes (default: ["read", "profile:read_all", "activity:read_all"])
  approvalPrompt?: "auto" | "force"; // Optional: Force re-authorization (default: "auto")
  accessType?: "offline" | "online"; // Optional: Refresh token support (default: "offline")
})

Email Handling

Important: Strava does not provide email addresses through their API.

This package automatically generates deterministic placeholder emails:

athlete-{athleteId}@strava.local

Example: [email protected]

Why This Approach?

  • Seamless: Works with Better Auth's required email field
  • Deterministic: Same athlete always gets the same email
  • Identifiable: Easy to detect with .endsWith("@strava.local")
  • No Configuration: Zero setup required

Handling in Your App

// Check if user needs to provide email
if (session.user.email.endsWith("@strava.local")) {
  // Show email collection form
}

// Filter real vs placeholder emails
const realUsers = users.filter((u) => !u.email.endsWith("@strava.local"));

Demo Application

This repository includes a full-featured demo app showing:

  • Complete authentication flow
  • Session management
  • User profile display
  • Modern UI with Shadcn components
  • PostgreSQL database integration

See Demo Setup Guide for installation instructions.

Project Structure

.
├── packages/
│   └── better-auth-strava/      # NPM package
│       ├── src/
│       │   └── index.ts         # Strava provider implementation
│       └── README.md
├── apps/
│   └── web/                     # Demo Next.js app
│       ├── app/
│       ├── lib/
│       └── components/
├── docker-compose.yml           # PostgreSQL for demo
└── README.md                    # This file

Resources

Contributing

Contributions are welcome! Please open an issue or submit a pull request.

License

MIT © Cristoper Anderson