Code Monkey home page Code Monkey logo

astro-auth's Introduction


Astro Auth

Getting Started

Run one of the following command inside your project directory to install the package:

yarn add @astro-auth/core

or

npm i @astro-auth/core

Documentation

Read the documentation at https://astro-auth.weoffersolution.com

Contributing

We're open to all community contributions, just make a pull request!

astro-auth's People

Contributors

abrahamx3 avatar aspiiire avatar feremabraz avatar max-programming avatar osadavc avatar ran-dall avatar riyaadh-abrahams avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

astro-auth's Issues

Credential Provider requires secret/private key

Error:
secretOrPrivateKey must have a value ../src/lib/oauth/client.ts:17:38354 Error: secretOrPrivateKey must have a value at jr (../src/lib/oauth/client.ts:17:38354) at ../src/lib/oauth/client.ts:25:128325 at Generator.next (<anonymous>) at s (../src/lib/oauth/client.ts:15:165) at processTicksAndRejections (node:internal/process/task_queues:96:5

Only Credential Provider is setup no other in the App.

Copied the exact code as Provided in the Credential Provider example in the docs

Email link provider

Hi!

Thanks for making this package! This makes working with Astro even better :)

For my use case I was able to get an email magic link flow working with the CredentialsProvider, which speaks to the amazing modularity of this package. But I think it would be even better if an EmailProvider was included in this project by default. If you'd be open to it, I would like to contribute create a PR for this. This is my proposal:

1) Send verification email

The flow starts by sending a verification token to the users email. This wil most likely be triggered from the client:

signIn({
  provider: 'email',
  sendToken: {
    email: 'EMAIL',
  },
});

2) Credentials hook

It would be nice if the developer could hook in to this step to allow/prevent sending the token. As I understand the signIn hook is triggered after authentication? So, it might make sense to introduce a new hook that is triggered before authenticating the user when providing credentials:

export const all = AstroAuth({
  authProviders: [
    // ...
  ],
  hooks: {
    credentials: async (credentials) => {
      if (credentials.provider === 'email') {
        const email = credentials.sendToken.email;
        return emailAllowList.includes(email);
      }

      if (credentials.provider === 'crendentials') {
        const email = credentials.login.email;
        return emailAllowList.includes(email);
      }

      // ...
    },
  },
});

This hook can be used to allow/deny credentials in the EmailProvider as well as the CredentialsProvider.

Another option is to reuse the signIn hook. This is what next-auth does. I personally think this is confusing, especially since the user object included in the arguments is just a placeholder in this case.

3) Generate Token

When the credentials are allowed the next step is to generate a token. My implementation generates a random string and persists that in a database, similar to how next-auth does it.

Pseudocode
import { nanoid } from 'nanoid';

async function generateToken(email: string) {
  const verificationToken = {
    email,
    token: nanoid(),
    createdAt: new Date(),
  };

  await persistVerificationToken(verificationToken);

  return verificationToken.token;
}

async function verifyToken(token: string, email: string) {
  const verificationToken = await getVerificationTokenFromDb(token);

  if (
    !verificationToken ||
    verificationToken.createdAt.getTime() + MAX_AGE < Date.now() ||
    verificationToken.email !== email
  ) {
    return false;
  }

  return true;
}

Another option is to encrypt a string including the email and createdAt using the ASTROAUTH_SECRET environment variable and use this as a token. This is similar to how remix-auth-email-link does it.

Pseudocode
import * as AES from 'crypto-js/aes';
import utf8Encoder from 'crypto-js/enc-utf8';

async function generateToken(email: string) {
  const verificationToken = {
    email,
    createdAt: Date.now(),
  };

  const encryptedToken = AES.encrypt(JSON.stringify(verificationToken), SECRET);

  return encryptedToken;
}

async function verifyToken(token: string, email: string) {
  let verificationToken: Record<string, any>;

  try {
    const json = AES.decrypt(token, SECRET).toString(utf8Encoder);
    verificationToken = JSON.parse(json);
  } catch {
    return false;
  }

  if (
    !verificationToken ||
    verificationToken.createdAt + MAX_AGE < Date.now() ||
    verificationToken.email !== email
  ) {
    return false;
  }

  return true;
}

I personally think for this library it makes more sense to go with the second option, since the whole adapter/persistence layer is not present atm. This flow could optionally be enriched by storing the encrypted token in a cookie and requiring the token in the cookie and the token send to the verification callback to be equal (this option is also provided in remix-auth-email-link).

4) Sending email

next-auth has nodemailer as an optional peer dependency and by default handles sending the email for you. Of course SMTP credentials need to be provided.

remix-auth-email-link requires the developer to define a sendEmail function to send the email.

I would personally like to go for the latter, even though it requires a bit more setup for the developer.

5) Verifying the token

I think it would make sense to add another auth endpoint called verify-email-token that requires the token as a search parameter and possibly an email parameter. I already added pseudocode for verifying the token in the generate token section.


I would love to hear your thoughts before I start working on a PR.

[Example] How to use CredentialProvider

import AstroAuth from "@astro-auth/core";
import { CredentialProvider } from "@astro-auth/providers";
import db from "../../../db";
import bcrypt from "bcryptjs"

type SignUpOptions = {
  email: string;
  password: string;
  confirmPassword: string;
  signUp: true;
}

type SignInOptions = {
  email: string;
  password: string;
}

type AuthOptions = SignUpOptions

const signUpHandler = async (db: any, { email, password, confirmPassword }: Omit<SignUpOptions, "signUp">) => {
  if (password !== confirmPassword) return false;
  const hashedPassword = await bcrypt.hash(password, 10)
  try {
    await db.user.create({
      data: {
        email,
        hashedPassword,
      }
    })
    return true;
  } catch (e) {
    return false
  }
}

const signInHandler = async (db: any, { email, password }: Pick<SignInOptions, "email"|"password">) => {
    const user = await db.user.findFirst({ where: { email } })
    if (!user) return false;
    const match = await bcrypt.compare(password, user.hashedPassword)
    if (!match) return false;
  
    return true;
}

export const all = AstroAuth({
  authProviders: [
    CredentialProvider({
       async authorize({ email, password, signUp, confirmPassword }: AuthOptions) {
        if (signUp) {
          return signUpHandler(db, { email, password, confirmPassword })
        }
  
        return signInHandler(db, { email, password })
  },
})
  ],
  hooks: {},
});

Assuming passwords are hashed with bcryptjs and there is a db (in this case it's prisma with sqlite)

Wrong URL fetched when logging in with oauth (google provider)

Hi!
Been testing this package and seemed like everything was working until I tried the signIn button (@astro-auth/client) in react from a more complex route.

Instead of making the request to /api/auth/signin as it is supposed to, it adds it after the current route, like /my/page/api/auth/signin, which returns a 404.

My hunch is that the fetch call is missing a leading / in the signIn function at const response = await fetch("api/auth/signin".

I tested by copying over the code and importing it locally, and it works when adding the /. (const response = await fetch('/api/auth/signin' )

I'm super new to Astro so let me know if it's wrong :) Hope this helps

Well, I want to authenticate with my backend server using credentials providers but I have no clues, so ! TwT

image

Frontend ( Svelte Code )

<script>
import { signIn } from "@astro-auth/client";


</script>

  <button type="submit" class="submit border border-black rounded-xl text-2xl font-normal leading-10 w-[400px] h-[59px] flex flex-row items-center justify-center bg-gray-400 disabled:bg-red-400"
    on:click|preventDefault={() => {
      signIn({
      provider: "credential",
      login: {
      email: "[email protected]",
      password: "test-password",
    },
  });
    }}
  >
  Log-In
  </button>
  </form>
  <p>Don't have an account?<a href="../create-account" style="color: #5A4FF3;"> Sign-Up</a></p>
  </div>
</div>

API route

import AstroAuth from "@astro-auth/core";
// Import the Credential provider(s)
import { CredentialProvider } from "@astro-auth/providers";

export const all = AstroAuth({
  authProviders: [
    CredentialProvider({
      authorize: "http://localhost:8000/login"
    }),
  ],
});

Facebook provider not working

Edit, even after checking that everything is updated by creating a new provider, It always returns "Jr: expected 200 OK, got: 401 Unauthorized" even after checking multiple times.

Facebook docs says that it need something like this:

https://www.facebook.com/v14.0/dialog/oauth?
  client_id={app-id}
  &redirect_uri={"https://www.domain.com/login"}
  &state={"{st=state123abc,ds=123456789}"}

I have created a new provider and imported it under [...astroauth].ts as the following example:

import { OAuthConfig, OAuthUserOptions } from '@astro-auth/types';

const FacebookProvider = (options: OAuthUserOptions): OAuthConfig => {
    return {
        id: 'facebook',
        name: 'Facebook',
        type: 'token',
        scope: 'email',
        options,
        authorization: 'https://www.facebook.com/v14.0/dialog/oauth',
        token: 'https://graph.facebook.com/oauth/access_token',
        userinfo: 'https://api.github.com/user',
        profile(profile) {
            return {
                id: profile.id.toString(),
                name: profile.name || profile.login,
                email: profile.email,
                image: profile.avatar_url,
                originalUser: { ...profile },
            };
        },
    };
};

export default FacebookProvider

I have added the redirect inside the facebook app to contain the required path present inside the docs, after login it get's stuck after receiving the code that I see in the params and returns Jr: expected 200 OK, got: 401 Unauthorized

Google provider works perfectly

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.