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-usos

v1.1.1

Published

USOS OAuth 1.0a authentication plugin for Better Auth

Readme

better-auth-usos

npm version Better Auth License: MIT TypeScript

USOS OAuth 1.0a authentication plugin for Better Auth.

What is USOS?

USOS (University Study-Oriented System) is a comprehensive academic system used by universities across Poland and other countries. It manages student records, course registration, grades, schedules, and more.

Universities Using USOS

This plugin works with any USOS instance. Some notable universities include:

  • Poland: Wrocław University of Science and Technology (PWR), University of Warsaw (UW), Warsaw University of Technology (PW), AGH University of Science and Technology, Jagiellonian University, and many more
  • Other countries with USOS deployments

Each university has its own USOS instance with a unique base URL (e.g., https://apps.usos.pwr.edu.pl).

Features

  • Full OAuth 1.0a flow implementation for USOS
  • Automatic user creation and updates
  • Configurable scopes and email domain
  • TypeScript support with full type definitions
  • Works with any USOS instance
  • Both server-side and client-side plugins

Installation

npm install better-auth-usos

Peer Dependencies

This plugin requires the following packages:

npm install better-auth crypto-js oauth-1.0a zod

Usage

1. Get USOS API Credentials

To use this plugin, you need to register your application with your university's USOS system:

  1. Go to your university's USOS Apps page (e.g., https://apps.usos.pwr.edu.pl)
  2. Navigate to "For developers" or "API" section
  3. Register a new application
  4. Set the callback URL to: https://yourdomain.com/api/auth/usos/callback
  5. Note your Consumer Key and Consumer Secret

2. Configure Environment Variables

Add these to your .env file:

USOS_BASE_URL=https://apps.usos.pwr.edu.pl
USOS_CONSUMER_KEY=your_consumer_key
USOS_CONSUMER_SECRET=your_consumer_secret
USOS_EMAIL_DOMAIN=student.pwr.edu.pl

3. Database Schema & Custom Fields

The plugin allows you to map USOS profile data to your custom user fields using the userFields option:

import { betterAuth } from "better-auth";
import { usosAuth } from "better-auth-usos";

export const auth = betterAuth({
  database: {
    // your database config
  },
  user: {
    additionalFields: {
      studentNumber: {
        type: "number",
        required: false,
      },
      usosId: {
        type: "string",
        required: false,
      },
      firstName: {
        type: "string",
        required: false,
      },
      lastName: {
        type: "string",
        required: false,
      },
    },
  },
  plugins: [
    usosAuth({
      usosBaseUrl: process.env.USOS_BASE_URL!,
      consumerKey: process.env.USOS_CONSUMER_KEY!,
      consumerSecret: process.env.USOS_CONSUMER_SECRET!,
      emailDomain: process.env.USOS_EMAIL_DOMAIN!,
      scopes: "studies|offline_access",
      userFields: (usosProfile) => ({
        studentNumber: usosProfile.student_number
          ? Number.parseInt(usosProfile.student_number)
          : null,
        usosId: usosProfile.id,
        firstName: usosProfile.first_name,
        lastName: usosProfile.last_name,
      }),
      onSuccess: async (user) => {
        console.log(user.firstName, user.lastName);
        return "/dashboard";
      },
    }),
  ],
});

The userFields function receives the USOS profile and returns an object with your custom fields. These fields will be:

  • Added when creating a new user
  • Updated when an existing user logs in
  • Available in the onSuccess callback with full TypeScript support

After configuration, run Better Auth migrations to create the database schema:

npx better-auth migrate

4. Client Setup

Add the client plugin to your Better Auth client:

import { createAuthClient } from "better-auth/client";
import { usosAuthClient } from "better-auth-usos/client";

export const authClient = createAuthClient({
  plugins: [usosAuthClient()],
});

5. Use in Your Application

Option 1: Using the client helper (Recommended)

import { authClient } from "./auth-client";

// React example
function LoginButton() {
  const handleLogin = async () => {
    await authClient.signIn.usos();
  };

  return <button onClick={handleLogin}>Login with USOS</button>;
}

// With options
function LoginWithOptions() {
  const handleLogin = async () => {
    await authClient.signIn.usos({
      callbackURL: "/dashboard", // Custom redirect after login
      newTab: false, // Open in new tab (optional)
    });
  };

  return <button onClick={handleLogin}>Login with USOS</button>;
}

Option 2: Direct redirect

function LoginButton() {
  const handleLogin = () => {
    window.location.href = "/api/auth/usos/login";
  };

  return <button onClick={handleLogin}>Login with USOS</button>;
}

Configuration Options

UsosAuthPluginOptions

| Option | Type | Required | Default | Description | | ---------------- | ------------------------------------------------------- | -------- | --------------------------- | ---------------------------------------------------------------------- | | usosBaseUrl | string | Yes | - | Base URL of your USOS instance | | consumerKey | string | Yes | - | OAuth consumer key from USOS | | consumerSecret | string | Yes | - | OAuth consumer secret from USOS | | emailDomain | string | Yes | - | Email domain for users without USOS email (e.g., student.pwr.edu.pl) | | scopes | string | No | "studies\|offline_access" | OAuth scopes to request | | userFields | (usosProfile: UsosUserProfile) => T | No | - | Function to map USOS profile to custom user fields | | onSuccess | (user: UsosAuthUser & T) => Promise<string> \| string | No | "/" | Redirect path after successful auth |

Available Scopes

Common USOS scopes include:

  • studies - Access to student data (courses, grades, etc.)
  • offline_access - Long-lived access tokens
  • email - Access to user's email
  • personal - Access to personal information
  • photo - Access to user's photo

Combine multiple scopes with |: "studies|offline_access|email"

Check your USOS instance documentation for available scopes.

User Data

Default Fields

The plugin automatically stores these standard Better Auth fields:

  • email - If available from USOS, otherwise generated as {student_number}@{emailDomain}
  • name - First name and last name combined
  • image - Profile photo 50x50 (if available)
  • emailVerified - Always true for USOS users

When a user doesn't have an email in USOS (common for students), the plugin generates one using their student number and the configured emailDomain. For example, if emailDomain is student.pwr.edu.pl and student number is 123456, the email will be [email protected].

Custom Fields with userFields

Use the userFields option to map any USOS profile data to your custom database fields. The function receives the full USOS profile:

interface UsosUserProfile {
  id: string;
  first_name: string;
  last_name: string;
  student_number: string | null;
  email: string | null;
  photo_urls?: {
    "50x50"?: string;
    "100x100"?: string;
  };
}

Example mapping common fields:

userFields: (usosProfile) => ({
  studentNumber: usosProfile.student_number
    ? Number.parseInt(usosProfile.student_number)
    : null,
  usosId: usosProfile.id,
  firstName: usosProfile.first_name,
  lastName: usosProfile.last_name,
});

These custom fields are automatically created/updated for every user login.

API Endpoints

The plugin creates these endpoints:

  • GET /api/auth/usos/login - Initiates OAuth flow
  • GET /api/auth/usos/callback - Handles OAuth callback

TypeScript

Full TypeScript support is included. Import types as needed:

import type {
  UsosAuthPluginOptions,
  UsosAuthUser,
  UsosUserProfile,
} from "better-auth-usos";

Example Projects

Full Stack Example

// auth.ts (server)
import { betterAuth } from "better-auth";
import { usosAuth } from "better-auth-usos";

export const auth = betterAuth({
  database: {
    provider: "pg",
    url: process.env.DATABASE_URL!,
  },
  user: {
    additionalFields: {
      studentNumber: {
        type: "number",
        required: false,
      },
      usosId: {
        type: "string",
        required: false,
      },
      firstName: {
        type: "string",
        required: false,
      },
      lastName: {
        type: "string",
        required: false,
      },
    },
  },
  plugins: [
    usosAuth({
      usosBaseUrl: process.env.USOS_BASE_URL!,
      consumerKey: process.env.USOS_CONSUMER_KEY!,
      consumerSecret: process.env.USOS_CONSUMER_SECRET!,
      emailDomain: process.env.USOS_EMAIL_DOMAIN!,
      userFields: (usosProfile) => ({
        studentNumber: usosProfile.student_number
          ? Number.parseInt(usosProfile.student_number)
          : null,
        usosId: usosProfile.id,
        firstName: usosProfile.first_name,
        lastName: usosProfile.last_name,
      }),
      onSuccess: async (user) => {
        console.log(`Welcome ${user.firstName} ${user.lastName}!`);
        return "/dashboard";
      },
    }),
  ],
});

// auth-client.ts (client)
import { createAuthClient } from "better-auth/client";
import { usosAuthClient } from "better-auth-usos/client";

export const authClient = createAuthClient({
  baseURL: "http://localhost:3000",
  plugins: [usosAuthClient()],
});

// login-page.tsx
import { authClient } from "./auth-client";

function LoginPage() {
  const handleLogin = async () => {
    await authClient.signIn.usos();
  };

  return (
    <div>
      <h1>Login</h1>
      <button onClick={handleLogin}>Login with USOS</button>
    </div>
  );
}

Client API

The client plugin provides a convenient signIn.usos() method:

await authClient.signIn.usos(options);

Options

| Option | Type | Default | Description | | ------------- | --------- | ------- | ------------------------------------------ | | callbackURL | string | - | Custom redirect URL after successful login | | newTab | boolean | false | Open login in a new tab |

Examples

// Basic usage
await authClient.signIn.usos();

// With custom callback
await authClient.signIn.usos({
  callbackURL: "/dashboard",
});

// Open in new tab
await authClient.signIn.usos({
  newTab: true,
});

Troubleshooting

"Failed to get request token"

  • Verify your consumer key and secret are correct
  • Check that your callback URL is registered in USOS
  • Ensure the USOS base URL is correct

"Missing OAuth state"

  • Check that cookies are enabled
  • Verify your site is using HTTPS in production
  • Check cookie settings (sameSite, secure)

"Invalid OAuth token"

  • This usually indicates a CSRF attack or cookie issues
  • Ensure cookies are properly configured
  • Check that the user completes the OAuth flow within 10 minutes

License

MIT

Contributing

Contributions are welcome! Please open an issue or pull request on GitHub.

Support

Credits

Created for the Polish university community using USOS systems.