All Projects → key-lab → blueauth

key-lab / blueauth

Licence: MIT license
🔐 Serverless passwordless authentication. No databases needed. Use in just 1 line in serverless, middleware, express, next.js, and more.

Programming Languages

typescript
32286 projects
javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to blueauth

demo-notes-app
Source for the demo notes app in the Serverless Stack Guide
Stars: ✭ 52 (-7.14%)
Mutual labels:  lambda
ExpressJS-SocketIO-Boilerplate
📦 Simple Express.js & Socket.io Boilerplate
Stars: ✭ 31 (-44.64%)
Mutual labels:  auth
eks
AWS EKS - kubernetes project
Stars: ✭ 149 (+166.07%)
Mutual labels:  lambda
laravel-nuxt
Laravel 5.6 + Nuxt 1.4: Auth Starter (SSR, SPA, Socialite)
Stars: ✭ 19 (-66.07%)
Mutual labels:  auth
lambda-pure
Pretty, minimal and fast ZSH prompt, with NodeJS version
Stars: ✭ 107 (+91.07%)
Mutual labels:  lambda
aws-tutorial-code
AWS tutorial code.
Stars: ✭ 114 (+103.57%)
Mutual labels:  lambda
auth-ajax
Auth token handling for Polymer
Stars: ✭ 15 (-73.21%)
Mutual labels:  auth
aws-sam-typescript-layers-example
Example project for developing AWS Lambda functions on TypeScript with all goodies: local development, tests, debugging, shared layers (3rd party and your own), and deploy.
Stars: ✭ 168 (+200%)
Mutual labels:  lambda
horse-basic-auth
Middleware for Basic Authentication in HORSE
Stars: ✭ 37 (-33.93%)
Mutual labels:  auth
formidable-serverless
Enables use of formidable (node.js module for parsing form data, especially file uploads) in serverless environments.
Stars: ✭ 28 (-50%)
Mutual labels:  lambda
aws-mobilehub-helper-ios
ARCHIVED: Use https://github.com/aws/aws-sdk-ios/
Stars: ✭ 41 (-26.79%)
Mutual labels:  auth
ertis-auth
Generic token generator and validator service like auth
Stars: ✭ 28 (-50%)
Mutual labels:  auth
serverless-plugin-bespoken
⚡ Serverless plugin to use our bst proxy tool
Stars: ✭ 29 (-48.21%)
Mutual labels:  lambda
jwt-auth
JSON Web Token Authentication for Thinkphp
Stars: ✭ 113 (+101.79%)
Mutual labels:  auth
kula
Lightweight and highly extensible .NET scripting language.
Stars: ✭ 43 (-23.21%)
Mutual labels:  lambda
firebase auth oauth
A Flutter plugin that makes it easy to perform OAuth sign in flows using FirebaseAuth.
Stars: ✭ 28 (-50%)
Mutual labels:  auth
cim
CIM takes the pain out of Infrastructure as Code and CloudFormation
Stars: ✭ 51 (-8.93%)
Mutual labels:  lambda
supabase-ui-svelte
Supabase authentication UI for Svelte
Stars: ✭ 83 (+48.21%)
Mutual labels:  auth
aws-nestjs-starter
Serverless, AWS, NestJS, GraphQL and DynamoDB starter
Stars: ✭ 200 (+257.14%)
Mutual labels:  lambda
yii-auth-client
Yii Framework external authentication via OAuth and OpenID Extension
Stars: ✭ 20 (-64.29%)
Mutual labels:  auth


BlueAuth

Simple and secure passwordless authentication.

One-line use in serverless, middleware, express, next.js, and more.

Table of Contents

Installation

npm install --save blueauth

Features

  • Get secure user authentication with just 1 line of code and a single configuration object. Done in minutes.
  • No third parties at all. Just you and your users. Complete control and security of your data.
  • No sensitive information to store, and no passwords to manage.
  • Stateless. No databases or connections required.
  • Run in express, next.js, aws lambda, or anywhere. More pre-built adapters coming soon.
  • Secure by default. Restrictive cookie policy, small surface area, and more.
  • Coded in typescript, complete with exposed type definitions and inline IDE documentation
  • Under 1k lines of code
  • Batteries included. Just add your desired settings, and BlueAuth handles everything else.

Quick Start

Quick example using Next.js. (Express and Lambda examples are below)

First create an API endpoint. We'll create one accessible at /api/blueauth:

// pages/api/blueauth.js

import { handler as blueauthHandler } from 'blueauth/nextjs';

// In this example our users are in this array,
// but in the real world you likely have them stored
// in an API service or DB
const users = [
  { id: '123', email: '[email protected]' },
  { id: '456', email: '[email protected]' },
];

// Now we will export the BlueAuth handler and pass it
// a single configuration object which has a secret key,
// SMTP email information, and two functions;
// findUniqueIdentity to retrieve a user, and
// createIdentity to create a user
export const handler = blueauthHandler({
  secret: 'mySecretForSigning',
  authEndpoint: 'https://myapp.com/api/blueauth', // used for links in emails
  smtpURL: 'smtps://postmaster:[email protected]/', // for sending emails
  smtpFromAddress: '[email protected]',
  findUniqueIdentity: async (payload) => {
    // This is the function used to try to find a given user / identity.
    // The payload here is what you sent to the API, which you will see below.
    // return a single found user / identity, or falsey if none found

    console.log('> looking for a user that matches', payload);

    const user = users.find((user) => (user.email === payload.email || user.id === payload.id));
    // Another example where we are getting users from an API;
    // const user = await fetch('https://secure-api.example-b.com/findUser', { method: 'post', body: JSON.stringify(payload) });

    if (user) {
      console.log('> found an existing user!', user);
      return user;
    } else {
      console.log('> could not find a matching user');
      return null;
    }
  },
  createIdentity: async (payload) => {
    // This is called when a registration is started via the API.

    console.log('> Creating a user with the following info', payload);

    const newId = '789';
    const newUser = { id: newId, ...payload };
    users.push(newUser);
    // const newUser = await fetch('https://secure-api.example-b.com/createUser', { method: 'post', body: JSON.stringify(payload) });
    return newUser;
  },
});

You now have an authentication (GraphQL) API service at /api/blueauth.

To make it simple to use, you can use the pre-built javascript client blueauth-client. Here's an example:

// pages/sign-in.jsx
import React, { useState } from 'react';
import blueauth from 'blueauth-client';

export default function Page() {
  const [email, setEmail] = useState('[email protected]');

  const handleRegister = async () => {
    // This will hit the createIdentity, with the results returned here.
    // By default this does not sign them in.
    // Can enable auto sign in the config options, or implement own logic.
    const { result } = await blueauth().register({ identity: { email } });
    console.log('> new user', result); // whatever is returned from createIdentity
  };

  const handleSignIn = async () => {
    // This will hit the findUniqueIdentity to find a user
    // If it returns a user, an email will be sent with a sign in link
    //   (to the user's email attribute)
    // after clicking the sign in link in the email, they will be sent to redirectURL (default of '/')
    const { result } = await blueauth().startEmailSignIn({
       identity: { email },
       redirectURL: '/dashboard'
    });
    console.log('> is sign in started:', result); // true or false
  };

  const handleRegisterOrSignIn = async () => {
    // This is a combination of register + start sign in.
    //
    // The back end library will first try to find a user with findUniqueIdentity
    // If it finds a user, it will send a sign in email
    // If it does not find a user, it will create one using createIdentity, then send a sign in email
    //   (or if signInAfterRegistration is set to true, a new user will be auto signed in)
    const { result } = await blueauth().registerOrStartEmailSignIn({
      identity: { email },
      redirectURL: '/dashboard'
    });
    // result is SIGN_IN_STARTED (or SIGN_IN_COMPLETED for new user auto sign in)
    console.log('> is new user or is sign in started?', result);
  };

  const handleWhoami = async () => {
    // This does an API call that uses the id stored in the secure cookie
    // and passes it to findUniqueIdentity to find the corresponding user.
    // in other words, in findUniqueIdentity payload is { id: 'someIdHere' }
    const { whoami } = await blueauth().getSelf();
    console.log('> you are', whoami); // whatever is returned from findUniqueIdentity
  };

  const handleSignOut = async () => {
    // This does an API call that deletes the cookie that stores the session information
    const { result } = await blueauth().signOut();
    console.log('> is signed out', result); // true
  };

  return (
    <div>
      <h1>sign in</h1>
      <input
        placeholder="[email protected]"
        type="email"
        onChange={(event) => setEmail(event.target.value)}
      />
      <button onClick={handleRegister}>Register</button>
      <button onClick={handleSignIn}>sign in</button>
      <button onClick={handleRegisterOrSignIn}>Start Register or Sign In</button>
      <button onClick={handleWhoami}>Who Am I?</button>
      <button onClick={handleSignOut}>Sign Out</button>
    </div>
  );
}

There is also blueauth-react that adds more features, such as tab syncing, server side rendering, a React provider, and more.

On the server side you can get the authenticated user with getIdentity.

// pages/api/secureEndpoint.js
import { getIdentity } from 'blueauth/nextjs';

const users = [
  // just another example stub for users
];

// can use the same config object from before,
// but only the secret and findUniqueIdentity are needed
const config = {
  secret: 'mySecretForSigning',
  findUniqueIdentity: (identityPayload) => {
    return users.find((user) => (user.email === payload.email || user.id === payload.id));
  },
};

export default async function handler(req, res) {
  // getIdentity grabs the secure cookie from the request, validates it,
  // and passes the cookie's payload to findUniqueIdentity to return a user
  const user = await getIdentity(config)({ req });

  if (!user) return res.status(400).json({ error: 'must be signed in!' });
  
  console.log('> user is', user);
  return res.status(200).json({
    userStuff: 'really secret',
    favoriteColor: user.favoriteColor
  });
}

In next.js you can use getIdentity in server side rendering on the pages.

Here's an example of a page that grabs the user in SSR, redirects users to sign in if not already, and has an example button that does an authenticated API request:

// pages/dashboard.jsx
import { getIdentity } from 'blueauth/nextjs';

export default function Page({ user }) {
  const handleUserClick = async () => {
    // credentials must be include to send the auth cookies along with the request
    const result = await fetch('/api/secureEndpoint', { credentials: 'include' });
    // (Don't forget to use getIdentity on the server side to retrive this request's user!)
    const jsonResults = await result.json();
    console.log('> your results are', { jsonResults });
  };

  return (
    <div>
      <p>Hi! Your email is {user.email}!</p>
      <button onClick={handleUserClick}>Click to do a request</button>
    </div>
  );
}

export const getServerSideProps = async (context) => {
  const config = { /* your config */ };
  const user = await getIdentity(config)(context);

  if (!user) return { redirect: { destination: '/sign-in' } };
  return { props: { user } };
};

blueauth-react can provide an identityContext to persist the identity across the app.

Documentation

Configuration

All settings and configuration is done by passing a single configuration object. Configuration object properties:

Name Default Type Description
secret string (required) Key to encrypt, decrypt, and sign data. Keep it secure. Changing will sign out all users.
findUniqueIdentity function (required) The function that takes a payload and returns a corresponding identity/user or falsely
createIdentity function (required) The function that takes a payload and creates a corresponding identity/user
authEndpoint string (required) The full URL to where the blueauth endpoint is. Primarily used for the links in sign in emails
smtpURL string (required) The SMTP URL for sending emails
smtpFromAddress string (required) The from email address for emails
smtpFromName Authentication string The from name in emails
smtpSubject Sign In string The subject for the sign in email
cookieNamePrefix blueauth string The prefix for the cookie used by blueauth
cookieOptions object The options for the auth cookie. Is merged with the default cookie options, with these settings taking priority, and passed to the underlying cookie library. Option documentation here
signInAfterRegistration false boolean Automatically sign in users upon registration
refreshSession false boolean Refresh/extend a user's session upon whoami checks. Otherwise they will have to re-sign in when their original sign in expires.
sessionLifespan 7 days string or number Set how long a user is signed in before having to re-sign in. Can be a string (like '7d' or '24h'), or a number in milliseconds.
serviceName null string The user facing service name. Used in the default email template.
createSignInEmailStrings function A function to create your own emails to send. Details below.

createSignInEmailStrings

If you want to define the body of the emails sent instead of using the default email templates, define this function in the config object. The function receives an object with the url key that is the URL the user must visit to be signed in and redirected. The function must return an object with a (required) text and (optional) html key for text and html emails. Example:

  const createSignInEmailStrings = ({ url }) => {
    return {
      text: `Please visit ${url} in your browser to sign in`,
      html: `Please click <a href="${url}">HERE</a> to sign in`,
    };
  };

Library API

There are two primary functions exported from the library for every platform adapter: handler and getIdentity.

handler

Is first passed a config object as defined above. It can then can be passed the relevant request and response objects (where applicable) in the platform, and delivers the proper response. The API of the handler varies between platforms (taking a request / response pair, a lambda event, etc.).

Under the hood, the handler simply transfers each of the various types of requests and responses into a common format to pass to the underlying library logic, and formats the response correctly for the platform

getIdentity

Is first passed a config object. Can pass the same config object that you pass to handler, but since only secret and findUniqueIdentity are required you can easily define a simple config anywhere. It can then be passed the relevant platform "request" object (or "event", etc.) and return a user.

Under the hood, for each platform getIdentity is simply getting the relevant cookie and passing it to the underlying library logic.

Exposed HTTP API

Everything is handled at a single URL endpoint, which is of course determined by where you mount the library according the the platform you are using.

There are 6 functionalities exposed by the API:

  • register a new identity
  • start a sign in flow
  • complete a sign in flow
  • register and/or start a sign in flow
  • who am I check
  • sign out

The "complete a sign in" flow is simply a GET request against the endpoint with a signInToken query parameter (which set to a secure token). This is typically used by the "sending a sign in email" functionality, which includes the endpoint with the signInToken query parameter as a link. The successful response to this request sets the auth cookie and redirects the user.

The remaining 5 functions are available through the endpoint as GraphQL queries. If you are unfamiliar with GraphQL, you can use the blueauth-client which wraps up all the API calls in a simple javascript library.

The GraphQL schema for these queries:

  scalar JSON

  enum ActionResult {
    SIGN_IN_STARTED
    SIGN_IN_COMPLETED
  }

  type Query {
    whoami: JSON
  }

  type Mutation {
    registerOrStartEmailSignIn(identity: JSON!, redirectURL: String): ActionResult!
    startEmailSignIn(identity: JSON!, redirectURL: String): Boolean!
    register(identity: JSON!): JSON!
    signOut: Boolean!
  }

Examples

Please go through the next.js Quick Start first the get a quick walkthrough as the first example.

Next.js

Quick Start

Express

import express from 'express';
import { getIdentity, handler } from 'blueauth/express';

const app = express();

const config = { /* your config */ };

app.use('/api/blueauth', handler(config));

app.get('/api/secureEndpoint', async (req, res) => {
  const user = await getIdentity(config)({ req });

  if (!user) return res.status(400).json({ error: 'must be signed in!' });

  return res.status(200).json({
    userStuff: 'really secret',
    favoriteColor: user.favoriteColor,
  });
});

app.listen(8080);

Lambda

import { handler } from 'blueauth/lambda';
const config = { /* your config */ };
export default async (event) => handler(config)(event);

Status

  • BlueAuth is being actively developed (and maintained).
  • The project is still maturing and as such there may be upgrades to the API or changes.
  • BlueAuth is being used in production (are you one of them?).
  • Aiming for a 1.0 release by September.

Contributing

Help build a simple and secure authentication future!

Contributions are very much desired! Official CONTRIBUTING.md coming soon. Use npx cz to use the commitizen CLI to create your commits according to project format. Maintained by Adrian Artiles and key.dev.

Use BlueAuth?

Promote your project!

Get in touch with Adrian or [email protected] Building a showcase to promote secure projects using BlueAuth. All welcomed!

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].