0.1.39 • Published 5 months ago

@passflow/passflow-react-sdk v0.1.39

Weekly downloads
-
License
MIT
Repository
github
Last release
5 months ago

@passflow/passflow-react-sdk

This is a SDK for react application.

Table of Contents

to install just type:

pnpm install
pnpm build

Local Development

Using Local Passflow JS SDK

For local development and testing with a local version of the Passflow JS SDK, you need to:

  1. Clone the Passflow JS SDK repository in a sibling directory to this project.
  2. remove current dependecy pnpm remove @passflow/passflow-js-sdk
  3. Link folder with:
pnpm link ../passflow-js-sdk
pnpm install

Now you can run watch mode in libraray mode and change it. It will compile every changes incrementally.

pnpm watch

After all done, we need to unlink and return all to the original state

pnpm remove @passflow/passflow-js-sdk
pnpm unlink @passflow/passflow-js-sdk
pnpm install @passflow/passflow-js-sdk

Test writing Environment Setup

For local development and UI testing, you need to set up the Passflow environment:

  1. Set the PASSFLOW_URL environment variable to point to your Passflow instance.
  2. Set the PASSFLOW_APP_ID environment variable
  3. Run pnpm dev anmd all should works

Refer .env.example for more details.

we are using pnpm. Please ansure you have it in the system.

UI Testing

We are using playwright to run UI tests.

First, ensure you have all runtime binary enabled:

pnpm exec playwright install

and then feel free to run the tests:

pnpm run test:ui

Writing your own ui tests.

You can find a tests in the ./tests frolder.

Please create the new files using the current tests as a reference.

To run the playwright in the design mode with ui, run the follwoing command:

pnpm playwright test --ui

Installation

pnpm add @passflow/passflow-react-sdk

Requirements

  • React 18+
  • React Router DOM v6/v7 or Wouter or TanStack Router

Integration

Passflow Cloud

For a quick start with Passflow Cloud:

const passflowConfig: PassflowConfig = {
  url: process.env.PASSFLOW_URL ?? 'http://localhost:5432',
  appId: process.env.PASSFLOW_APP_ID ?? 'test_app_id',
  createTenantForNewUser: true,
  scopes: ['openid', 'email', 'profile', 'offline'],
};

export const PassflowProviderWrapper: FC<PropsWithChildren> = ({
  children,
}) => {
  const navigate = useNavigate(); // from react-router-dom

  return (
    <PassflowProvider
      url={passflowConfig.url}
      appId={passflowConfig.appId}
      createTenantForNewUser={passflowConfig.createTenantForNewUser}
      scopes={passflowConfig.scopes}
      navigate={(options) =>
        navigate(
          {
            pathname: options.to,
            search: options.search,
          },
          { replace: options.replace }
        )
      }
      router="react-router"
    >
      {children}
    </PassflowProvider>
  );
};

export const App = () => (
  <BrowserRouter>
    <PassflowProviderWrapper>
      <PassflowFlow
        federatedDisplayMode='redirect'
        successAuthRedirect='https://jwt.io'
        pathPrefix='/web'
      />
    </PassflowProviderWrapper>
  </BrowserRouter>
);

PassflowFlow

PropTypeDescription
successAuthRedirectstringURL to redirect after successful authentication
federatedDisplayMode"popup" | "redirect"Federated authentication display mode
pathPrefixstringPrefix for all routes (optional)

React Router DOM

Example of integration with React Router DOM: PS: The example uses the Declarative approach.

import {
  PassflowProvider,
  SignIn,
  SignUp,
  ForgotPassword,
  ForgotPasswordSuccess,
} from "@passflow/passflow-react-sdk";
import { BrowserRouter, Routes, Route, useNavigate } from "react-router-dom";

const passflowConfig = {
  url: import.meta.env.VITE_PASSFLOW_URL,
  appId: import.meta.env.VITE_PASSFLOW_APP_ID,
  createTenantForNewUser: true,
  scopes: ["id", "offline", "email", "profile", "openid", "management"],
};

const PassflowProviderWrapper = ({ children }) => {
  const navigate = useNavigate();

  return (
    <PassflowProvider
      url={passflowConfig.url}
      appId={passflowConfig.appId}
      createTenantForNewUser={passflowConfig.createTenantForNewUser}
      scopes={passflowConfig.scopes}
      navigate={(options) =>
        navigate(
          {
            pathname: options.to,
            search: options.search,
          },
          { replace: options.replace }
        )
      }
      router="react-router"
    >
      {children}
    </PassflowProvider>
  );
};

export const App = () => (
  <BrowserRouter>
    <PassflowProviderWrapper>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/signin" element={<SignIn successAuthRedirect="/" signUpPath="/signup" />} />
        <Route path="/signup" element={<SignUp successAuthRedirect="/" signInPath="/signin" />} />
        <Route 
          path="/forgot-password" 
          element={
            <ForgotPassword
              successResetRedirect="/"
              signInPath="/signin"
              forgotPasswordSuccessPath="/forgot-password/success"
            />
          } 
        />
        <Route path="/forgot-password/success" element={<ForgotPasswordSuccess />} />
        {/* Add other routes here */}
      </Routes>
    </PassflowProviderWrapper>
  </BrowserRouter>
);

Wouter

Example of integration with Wouter:

import {
  PassflowProvider,
  SignIn,
  SignUp,
  ForgotPassword,
  ForgotPasswordSuccess,
} from "@passflow/passflow-react-sdk";
import { Switch, Route, useLocation } from "wouter";

const passflowConfig = {
  url: import.meta.env.VITE_PASSFLOW_URL,
  appId: import.meta.env.VITE_PASSFLOW_APP_ID,
  createTenantForNewUser: true,
  scopes: ["id", "offline", "email", "profile", "openid", "management"],
};

const PassflowProviderWrapper = ({ children }) => {
  const [, navigate] = useLocation();

  return (
    <PassflowProvider
      url={passflowConfig.url}
      appId={passflowConfig.appId}
      createTenantForNewUser={passflowConfig.createTenantForNewUser}
      scopes={passflowConfig.scopes}
      navigate={(options) => {
        const searchParamWouter = options.search
          ? options.search.startsWith("?")
            ? options.search
            : `?${options.search}`
          : "";
        navigate(`${options.to}${searchParamWouter}`, {
          replace: options.replace,
        });
      }}
      router="wouter"
    >
      {children}
    </PassflowProvider>
  );
};

export const App = () => (
  <Switch>
    <PassflowProviderWrapper>
      <Route path="/" component={Home} />
      <Route path="/signin" component={SignInWrapper} />
      <Route path="/signup" component={SignUpWrapper} />
      <Route path="/forgot-password" component={ForgotPasswordWrapper} />
      <Route path="/forgot-password/success" component={ForgotPasswordSuccessWrapper} />
      {/* Add other routes here */}
    </PassflowProviderWrapper>
  </Switch>
);

TanStack Router

Example of integration with TanStack Router: PS: The example uses Code-Based Routing.

// App.tsx
import { PassflowProviderWrapper, RouterProvider } from './providers';

export const App = () => (
  <PassflowProviderWrapper>
    <RouterProvider />
  </PassflowProviderWrapper>
);

// PassflowProviderWrapper.tsx
import { PassflowProvider } from '@passflow/passflow-react-sdk';

const passflowConfig = {
  url: import.meta.env.VITE_PASSFLOW_URL,
  appId: import.meta.env.VITE_PASSFLOW_APP_ID,
  createTenantForNewUser: true,
  scopes: ['id', 'offline', 'email', 'profile', 'openid', 'management'],
};

export const PassflowProviderWrapper = ({ children }) => (
  <PassflowProvider
    url={passflowConfig.url}
    appId={passflowConfig.appId}
    createTenantForNewUser={passflowConfig.createTenantForNewUser}
    scopes={passflowConfig.scopes}
    router="tanstack-router"
  >
    {children}
  </PassflowProvider>
);

// router/root.tsx
import { useNavigation } from '@passflow/passflow-react-sdk';
import { Outlet, useNavigate } from '@tanstack/react-router';

export const Root = () => {
  const navigate = useNavigate();
  const { setNavigate } = useNavigation();

  useEffect(() => {
    setNavigate((options) => navigate(options));
  }, [navigate, setNavigate]);

  return <Outlet />;
};

// router.tsx
import { QueryClient } from '@tanstack/react-query';
import { createRouter } from '@tanstack/react-router';
import { queryClient } from '../query';
import { routerTree } from './routes';
import { Passflow } from '@passflow/passflow-react-sdk';

export interface RouterContext {
  queryClient: QueryClient;
  passflow?: Passflow;
}

export const router = createRouter({
  routeTree: routerTree,
  context: {
    queryClient,
    passflow: undefined,
  },
  defaultPreload: 'intent',
});

declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router;
  }
}


// routes.tsx
import { Outlet, createRootRouteWithContext, createRoute, redirect } from '@tanstack/react-router';
import { RouterContext } from './router';
import { Root } from './root';
import { About, Home } from '@/pages';
import { ForgotPassword, ForgotPasswordSuccess, SignIn, SignUp } from '@passflow/passflow-react-sdk';
import { RootLayout } from '@/layouts';

const redirectToSignin = () => {
  throw redirect({
    to: '/signin',
  });
};

const rootRoute = createRootRouteWithContext<RouterContext>()({
  component: Root,
  notFoundComponent: () => <div>404 Not Found</div>,
});

// PUBLIC ROUTES
const publicRoute = createRoute({
  getParentRoute: () => rootRoute,
  id: 'public',
  component: () => <Outlet />,
});

const signInRoute = createRoute({
  getParentRoute: () => publicRoute,
  path: '/signin',
  component: () => <SignIn successAuthRedirect='/' signUpPath='/signup' />,
});

const signUpRoute = createRoute({
  getParentRoute: () => publicRoute,
  path: '/signup',
  component: () => <SignUp successAuthRedirect='/' signInPath='/signin' />,
});

const forgotPasswordRoute = createRoute({
  getParentRoute: () => publicRoute,
  path: '/forgot-password',
  component: () => (
    <ForgotPassword successResetRedirect='/' signInPath='/signin' forgotPasswordSuccessPath='/forgot-password/success' />
  ),
});

const forgotPasswordSuccessRoute = createRoute({
  getParentRoute: () => publicRoute,
  path: '/forgot-password/success',
  component: () => <ForgotPasswordSuccess />,
});

{/* Add other PASSFLOW COMPONENTS routes here */}

// PROTECTED ROUTES
const protectedRoute = createRoute({
  getParentRoute: () => rootRoute,
  id: 'protected',
  beforeLoad: async ({ context }) => {
    const { passflow } = context;

    await passflow?.session({
      createSession: async (tokens) => {
        console.log(tokens); // if session is created, this function will be called with the tokens
      },
      expiredSession: async () => {
        console.log('expiredSession');
        redirectToSignin(); // if session is expired and refresh token is not valid, redirect to signin
      },
      doRefresh: true,
    });
  },
  component: () => <RootLayout />,
});

const dashboardRoute = createRoute({
  getParentRoute: () => protectedRoute,
  path: '/',
  component: () => <Home />,
});

const aboutRoute = createRoute({
  getParentRoute: () => protectedRoute,
  path: '/about',
  component: () => <About />,
});

{/* Add other protected routes here */}

export const routerTree = rootRoute.addChildren([
  publicRoute.addChildren([signInRoute, signUpRoute, forgotPasswordRoute, forgotPasswordSuccessRoute]),
  protectedRoute.addChildren([dashboardRoute, aboutRoute]),
]);

Props

PassflowProvider

PropTypeDescription
urlstringPassflow server URL
appIdstringApplication ID
createTenantForNewUserbooleanWhether to create a tenant for new users
scopesstring[]Array of required scopes
router"default" | "react-router" | "wouter" | "tanstack-router"Router being used (optional) (default is native window navigation)
navigate(options: NavigateOptions) => voidNavigation function (optional) (default is native window navigation)

Form Components

SignIn

Component for user authentication.

PropTypeDescriptionDefault
successAuthRedirectstringURL to redirect after successful sign inRequired
signUpPathstringPath to sign up page (optional)/signup
forgotPasswordPathstringPath to forgot password page (optional)/forgot-password
verifyMagicLinkPathstringPath to verify magic link page (optional)/verify-challenge-magic-link
verifyOTPPathstringPath to verify OTP page (optional)/verify-challenge-otp
federatedDisplayMode"popup" | "redirect"Display mode for federated authentication (optional)"popup"

SignUp

Component for user registration.

PropTypeDescriptionDefault
successAuthRedirectstringURL to redirect after successful sign upRequired
signInPathstringPath to sign in page (optional)/signin
verifyMagicLinkPathstringPath to verify magic link page (optional)/verify-challenge-magic-link
verifyOTPPathstringPath to verify OTP page (optional)/verify-challenge-otp
federatedDisplayMode"popup" | "redirect"Display mode for federated authentication (optional)"popup"

ForgotPassword

Component for password recovery initiation.

PropTypeDescriptionDefault
successResetRedirectstringURL to redirect after successful password resetRequired
signInPathstringPath to sign in page (optional)/signin
forgotPasswordSuccessPathstringPath to success page after initiating password reset (optional)/forgot-password/success

ForgotPasswordSuccess

Component for password recovery success.

No props required.

ResetPassword

Component for setting a new password.

PropTypeDescriptionDefault
successAuthRedirectstringURL to redirect after successful password resetRequired

VerifyChallengeMagicLink

Component for verifying magic link authentication.

No props required.

VerifyChallengeOTP

Component for OTP verification.

PropTypeDescriptionDefault
successAuthRedirectstringURL to redirect after successful verificationRequired
numInputsnumberNumber of OTP input fields (optional)6
shouldAutoFocusbooleanWhether to autofocus the first input (optional)true
signUpPathstringPath to sign up page (optional)/signup

InvitationJoin

Component for accepting invitations and joining organizations.

PropTypeDescriptionDefault
successAuthRedirectstringURL to redirect after successful joinRequired
signInPathstringPath to sign in page (optional)/signin

Hooks

useAuth

Hook for authentication management. Provides methods for checking authentication status, obtaining tokens, and logging out.

const { isAuthenticated, getTokens, logout, isLoading } = useAuth();
ParameterTypeDescription
initialRefreshboolean (optional)Whether to refresh tokens on mount

Returns:

PropertyTypeDescription
isAuthenticated() => booleanCurrent authentication status
getTokens(doRefresh: boolean) => Promise<{ tokens: Tokens \| undefined; parsedTokens: ParsedTokens \| undefined; }>Function to get authentication tokens
logout() => voidFunction to log out user
isLoadingbooleanLoading state indicator

usePassflow

Hook for accessing the Passflow SDK instance. Must be used within PassflowProvider.

const passflow = usePassflow();

Returns:

TypeDescription
PassflowPassflow SDK instance

usePassflowStore

Hook for synchronizing state with Passflow SDK. Allows subscribing to token changes.

const tokens = usePassflowStore([PassflowEvent.SignIn, ...]);
ParameterTypeDescription
eventsPassflowEvent[] (optional)Events to subscribe to

Returns:

TypeDescription
Tokens \| undefinedCurrent tokens state

useSignIn

Hook for implementing sign-in functionality. Supports password, passkey, and passwordless authentication.

const { fetch, isLoading, isError, error } = useSignIn();

Returns:

PropertyTypeDescription
fetch(payload: PassflowPasskeyAuthenticateStartPayload \| PassflowSignInPayload \| PassflowPasswordlessSignInPayload, type: 'passkey' \| 'password' \| 'passwordless') => Promise<boolean \| string \| PassflowPasswordlessResponse>Sign in function
isLoadingbooleanLoading state
isErrorbooleanError state
errorstringError message

useSignUp

Hook for implementing registration functionality. Supports password, passkey, and passwordless registration.

const { fetch, isLoading, isError, error } = useSignUp();

Returns:

PropertyTypeDescription
fetch(payload: PassflowPasskeyRegisterStartPayload \| PassflowSignUpPayload \| PassflowPasswordlessSignInPayload, type: 'passkey' \| 'password' \| 'passwordless') => Promise<boolean \| PassflowPasswordlessResponse>Sign up function
isLoadingbooleanLoading state
isErrorbooleanError state
errorstringError message

useNavigation

Hook for navigation between pages. Supports various routers (react-router, wouter, tanstack-router).

const { navigate, setNavigate } = useNavigation();

Returns:

PropertyTypeDescription
navigateNavigateFunctionNavigation function
setNavigate(newNavigate: NavigateFunction \| null) => voidFunction to update navigation handler

useProvider

Hook for working with federated authentication providers (OAuth).

const { federatedWithPopup, federatedWithRedirect } = useProvider(redirectUrl);
ParameterTypeDescription
redirectUrlstringURL to redirect after authentication

Returns:

PropertyTypeDescription
federatedWithPopup(provider: Providers) => voidPopup authentication function
federatedWithRedirect(provider: Providers) => voidRedirect authentication function

useResetPassword

Hook for resetting user password.

const { fetch, isLoading, isError, error } = useResetPassword();

Returns:

PropertyTypeDescription
fetch(newPassword: string) => Promise<boolean>Password reset function
isLoadingbooleanLoading state
isErrorbooleanError state
errorstringError message

useUserPasskeys

Hook for managing user passkeys (create, edit, delete).

const { data, createUserPasskey, editUserPasskey, deleteUserPasskey } = useUserPasskeys();

Returns:

PropertyTypeDescription
dataPassflowUserPasskey[]List of user passkeys
createUserPasskey(relyingPartyId: string) => Promise<void>Create passkey function
editUserPasskey(newName: string, passkeyId: string) => Promise<void>Edit passkey function
deleteUserPasskey(passkeyId: string) => Promise<void>Delete passkey function
isLoadingbooleanLoading state
isErrorbooleanError state
errorMessagestringError message

useAppSettings

Hook for retrieving application settings and password policies.

const { appSettings, passwordPolicy } = useAppSettings();

Returns:

PropertyTypeDescription
appSettingsAppSettings \| nullApplication settings
passwordPolicyPassflowPasswordPolicySettings \| nullPassword policy settings
isLoadingbooleanLoading state
isErrorbooleanError state
errorstringError message

useAuthCloudRedirect

Hook for redirecting to Passflow Cloud.

const redirect = useAuthCloudRedirect(cloudPassflowUrl);
ParameterTypeDescription
cloudPassflowUrlstringPassflow Cloud URL

Returns:

TypeDescription
() => voidRedirect function

useForgotPassword

Hook for initiating the password recovery process.

const { fetch, isLoading, isError, error } = useForgotPassword();

Returns:

PropertyTypeDescription
fetch(payload: PassflowSendPasswordResetEmailPayload) => Promise<boolean>Password recovery function
isLoadingbooleanLoading state
isErrorbooleanError state
errorstringError message

useJoinInvite

Hook for accepting organization invitations.

const { fetch, isLoading, isError, error } = useJoinInvite();

Returns:

PropertyTypeDescription
fetch(token: string) => Promise<boolean>Join invitation function
isLoadingbooleanLoading state
isErrorbooleanError state
errorstringError message

useLogout

Hook for logging out of the system.

const { fetch, isLoading, isError, error } = useLogout();

Returns:

PropertyTypeDescription
fetch() => Promise<boolean>Logout function
isLoadingbooleanLoading state
isErrorbooleanError state
errorstringError message

usePasswordlessComplete

Hook for completing passwordless authentication.

const { fetch, isLoading, isError, error } = usePasswordlessComplete();

Returns:

PropertyTypeDescription
fetch(payload: PassflowPasswordlessSignInCompletePayload) => Promise<PassflowValidationResponse \| null>Complete passwordless auth function
isLoadingbooleanLoading state
isErrorbooleanError state
errorstringError message
0.1.39

5 months ago

0.1.38

5 months ago

0.1.37

5 months ago

0.1.36

6 months ago

0.1.35

6 months ago

0.1.34

6 months ago

0.1.33

6 months ago

0.1.32

6 months ago

0.1.31

6 months ago

0.1.30

6 months ago

0.1.29

6 months ago

0.1.28

6 months ago

0.1.27

7 months ago

0.1.26

7 months ago

0.1.25

7 months ago

0.1.24

7 months ago

0.1.23

7 months ago

0.1.22

7 months ago

0.1.21

7 months ago

0.1.20

7 months ago

0.1.19

7 months ago

0.1.18

7 months ago

0.1.17

7 months ago

0.1.16

7 months ago

0.1.15

7 months ago

0.1.14

7 months ago

0.1.12

7 months ago

0.1.11

7 months ago

0.1.10

7 months ago

0.1.9

7 months ago

0.1.8

8 months ago

0.1.7

8 months ago

0.1.6

8 months ago

0.1.5

8 months ago

0.1.4

8 months ago

0.1.2

8 months ago

0.0.2

10 months ago

0.0.1

10 months ago