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

ember-better-auth

v0.1.0

Published

Integrate better-auth with ember.js

Downloads

3

Readme

ember-better-auth

Integrates better-auth with Ember's reactivity system, while at the same time mimicing the API from ember-simple-auth.

Compatibility

  • Ember.js with vite

Installation

ember install ember-better-auth better-auth

Example

This repo contains an example for a server with Hono and a web app with Ember.

Setup

This setup expects the server for better-auth is already done and operationg on http://localhost:3000. This setup will guide you configuring better-auth for ember on the client side.

1. Auth Service

ember-better-auth provides an AuthService that you need to use and configure to your liking and load the service according to the Strict Reslover RFC.

A) Use the Registry

ember-better-auth ships with a registry function for usage with the ember-strict-application-resolver package.

// src/app.ts
import EmberApp from 'ember-strict-application-resolver';

import { authRegistry } from 'ember-better-auth/registry';

class Router extends EmberRouter {
  location = 'history';
  rootURL = '/';
}

Router.map(function () {
  // your routes
});

export default class App extends EmberApp {
  modules = {
    './router': { default: Router },
    ...authRegistry(),
    ...import.meta.glob('./services/**/*', { eager: true }),
    ...import.meta.glob('./routes/**/*', { eager: true }),
    ...import.meta.glob('./templates/**/*', { eager: true })
  };
}

B) Your own Service (for your own User type)

This is great if you already have a User type you want to use, and/or you defined custom fields for your user, then this is a way to expand the types provided by better-auth

// src/domain/user.ts
import type { BetterAuthUser } from 'ember-better-auth';

export interface User extends BetterAuthUser {
  name: string;
  email: string;
  emailVerified: boolean;
  givenName?: string;
  familyName?: string;
}

Then extend the auth service and configure it with the User type.

// src/services/auth.ts
import { AuthService as UpstreamAuthService } from 'ember-better-auth';

import type { User } from '../domain/user';

export default class AuthService extends UpstreamAuthService<User> {}

declare module '@ember/service' {
  interface Registry {
    auth: AuthService;
  }
}

Don't forget to load this service as part of your registry.

2. Auth Client

Start by creating an authClient:

// src/auth.ts
import { createAuthClient } from 'better-auth/client';

// here you configure your auth client, eg. plugins you plan to use
export const auth = createAuthClient({
  baseURL: 'http://localhost:3000'
});

3. Connect Auth Client with Auth Service

Now that the client and the service are ready, let's connect them, so they know each other. Here again, there are two places in which you can do that

A) In ApplicationRoute

That's the place you know where to configure your app:

// src/routes/application.ts
import Route from '@ember/routing/route';
import { service } from '@ember/service';
import { auth } from '../auth';

import type AuthService from '../services/auth';

export default class ApplicationRoute extends Route {
  @service('auth') declare authService: AuthService;

  async beforeModel() {
    authService.setup(auth);
  }
}

B) Use App config

A slightly different approach is to stop the autoboot for your app and configure it a bit more verbose:

// src/app.ts

// see above

export function createApp(options: Record<string, unknown> = {}) {
  const app = App.create({ ...options, autoboot: false });

  return app.buildInstance();
}

export async function start(instance: ApplicationInstance) {
  await instance.boot();

  instance.startRouting();
}

then start your app in index.html:

<!DOCTYPE html>
<html>
  <head>
    <!-- snip-->
  </head>
  <body>
    <script type="module">
      import { createApp, start } from './src/app';
      import { configure } from './src/config';

      const app = createApp();

      configure(app);
      start(app);
    </script>
  </body>
</html>

And then have a dedicated config:

// src/config.ts
import { auth } from './auth';

import type ApplicationInstance from '@ember/application/instance';

function configureAuth(app: ApplicationInstance) {
  const authService = app.lookup('service:auth');

  authService.setup(auth);
}

export function configure(app: ApplicationInstance) {
  configureAuth(app);

  // more configs, yay :party:
  // configureIntl(app)
}

Usage

For most parts, you can import the auth client directly and follow the better-auth documentation.

The interesting bit is that the auth client is connected with the AuthService which itself tracks a users logged in state and can react to it.

Showing the Logged in User

So here is the application route showing the logged in user in the header of the Page.

// src/templates/application.gts
import Component from '@glimmer/component';
import { service } from '@ember/service';

import type AuthService from '../services/auth';

export default class ApplicationRoute extends Component {
  @service declare auth: AuthService;

  <template>
    <header>
      {{#if this.auth.authenticated}}
        Hi {{this.auth.user.name}}

        <Link @href="/user/profile">Profile</Link>
        <Link @href="/user/sessions">Sessions</Link>

        <Link @href="/user/logout">Logout</Link>
      {{else}}
        <Link @href="/login">Login</Link>
      {{/if}}
    </header>

    {{outlet}}
  </template>
}

Login

Here is a login route using the action() from ember-command:

// src/templates/login.gts
import { action } from 'ember-command';

import { FocusPage, Form } from '@hokulea/ember';

import { auth } from '../auth';

const login = action(
  ({ services: { router } }) =>
    async (data: { email: string; password: string }) => {
      await auth.signIn.email(data, {
        onSuccess: () => {
          router.transitionTo('user.profile');
        }
      });
    }
);

<template>
  <FocusPage @title="Login">
    <Form @submit={{(login)}} as |f|>
      <f.Email @name="email" @label="Email" required />

      <f.Password @name="password" @label="Password" required />

      <f.Submit>Login</f.Submit>
    </Form>
  </FocusPage>
</template>

Logout

Logging out people using the logout route:

// src/routes/user/logout.ts
import Route from '@ember/routing/route';
import { service } from '@ember/service';

import type AuthService from '../../services/auth';

export default class LogoutRoute extends Route {
  @service declare auth: AuthService;

  async beforeModel() {
    await this.auth.client.signOut({
      fetchOptions: {
        onSuccess: () => {
          this._router.transitionTo('application');
        }
      }
    });
  }
}

Protected Routes

The auth service can verify if the user is logged in and allow routing based on that check. From the example above, let's make every /user/* route protected for only logged in users

// src/routes/user.ts
import Route from '@ember/routing/route';
import { service } from '@ember/service';

import type AuthService from '../services/auth';
import type Transition from '@ember/routing/transition';

export default class UserRoute extends Route {
  @service declare auth: AuthService;

  async beforeModel(transition: Transition) {
    const authenticated = await this.auth.requireAuthentication(transition);

    if (!authenticated) {
      this._router.transitionTo('login');
    }
  }
}

Reacting to Login/Logout Events

The AuthService emits events on certain ocasions you can subscribe to. For example to route people after login/logout.

// in your configure

function configureAuth(auth: AuthService) {
  auth.subscribe('sessionAuthenticated', (user) => {
    // do sth with `user`
  });

  auth.subscribe('sessionInvalidated', () => {
    // user is logged out - maybe route them?
  });
}