@triare/auth-redux
TypeScript icon, indicating that this package has built-in type declarations

0.2.9 • Public • Published

Beta version

The package is designed for internal use by TRIARE. https://triare.net

The package is free and can be freely distributed

The package is written in conjunction with other company products and is part of a comprehensive solution.

How to use

Run

npm i @triare/auth-redux
yarn add @triare/auth-redux

To use it in a React project you need to create a store directory and create 2 files in it

Create files (store/index.ts and store/auth/index.ts)

Copy

store/index.ts

// example

import { combineReducers, configureStore } from '@reduxjs/toolkit';
import createSagaMiddleware from 'redux-saga';
import { spawn } from 'redux-saga/effects';
import { EnhancedStore } from '@reduxjs/toolkit/src/configureStore';
import { moduleName as authModuleName, reducer as authReducer, saga as authSaga } from './auth';
import { moduleName as userModuleName, reducer as userReducer, saga as userSaga } from './user';
import { moduleName as commonModuleName, reducer as commonReducer, saga as commonSaga } from './common';

export const sagaMiddleware = createSagaMiddleware();

const store: EnhancedStore = configureStore({
  reducer: combineReducers({
    [authModuleName]: authReducer,
    [userModuleName]: userReducer,
    [commonModuleName]: commonReducer,
  }),
  middleware: (getDefaultMiddleware) => getDefaultMiddleware({
    thunk: false,
  }).concat([sagaMiddleware]),
  devTools: process.env.REACT_APP_ENV !== 'production',
});

export type RootState = ReturnType<typeof store.getState>;

sagaMiddleware.run(function* runSaga() {
  yield spawn(authSaga, store);
  yield spawn(commonSaga, store);
  yield spawn(userSaga);
});

export default store;

All parameters are described in the @triare/auth-redux/src/config.ts file

Reading descriptions are recommended before usage

Copy

store/auth/index.ts

// example

/* eslint-disable @typescript-eslint/no-unused-vars, no-param-reassign */

import {
  config,
  createReducers,
  State as AuthState,
  setActions,
  saga as authSaga,
  updateConfig,
  getLocalState,
  Action,
  FETCH_ME_AFTER_SUCCESS_AUTHORIZATION, createUrl,
} from '@triare/auth-redux';
import { createSlice, SliceCaseReducers } from '@reduxjs/toolkit';
import { spawn, takeLatest, put } from 'redux-saga/effects';
import { EnhancedStore } from '@reduxjs/toolkit/src/configureStore';
import { useSelector } from 'react-redux';
import { OTPResponse } from '@triare/auth-redux/dist/saga/auth/OTP';
import { SignInAction } from '@triare/auth-redux/dist/saga/auth/signIn';
import { UrlObject } from '@triare/auth-redux/src/config';
import { PayloadAction } from '@triare/auth-redux/src/saga/auth';
import { AnyObject } from '@triare/auth-redux/src/saga/common';
import { User as AuthUser } from '@triare/auth-redux/dist/saga/auth/useMe';
import { RootState } from '../index';

export * from '@triare/auth-redux';

export default updateConfig({
  fetchDelay: parseInt(process.env.REACT_APP_FETCH_DELAY || '0', 10),
  api: {
    url: process.env.REACT_APP_API || '',
  },
  signIn: {
    ...config.signIn,
    requestBody: {
    byEmail: ({ remember, ...data }: SignInAction) => JSON.stringify(data),
    byUsername: ({ remember, ...data }: SignInAction) => JSON.stringify(data),
    byPhone: ({ remember, ...data }: SignInAction) => JSON.stringify(data),
    byService: ({ method, remember, ...data }: SignInAction) => JSON.stringify(data),
  },
  fetchMeAfterSuccess: FETCH_ME_AFTER_SUCCESS_AUTHORIZATION,
  },
  OTP: {
    ...config.OTP,
    fetchMeAfterSuccess: false,
  },

  createUrl: (
    url: string | UrlObject,
    addToUrl?: string,
    payload?: PayloadAction,
    data?: AnyObject,
    _query?: AnyObject,
  ) => createUrl(url, addToUrl, payload, data, {
    ..._query,
    lang: 'en',
  }),
});

export const moduleName = config.auth.name;

export interface User extends AuthUser {
  firstName: string;
  lastName: string;
  email: string;
  phone: string;
  settings: {
    isEmailVerified: boolean;
    isPhoneVerified: boolean;
  };
}

export interface State extends AuthState {
  user: User | null;
  secretKey: string;
}

export function useAuth(): State {
  return useSelector((state: RootState) => state[moduleName]);
}

export const auth = createSlice<State, SliceCaseReducers<State>, string, SliceSelectors<State>>({
  name: moduleName,
  initialState: getLocalState<State>(),
  reducers: {
    ...createReducers(),
    signInSuccess: (state, { payload }) => {
      Object.assign(state, payload);

      state.signIn.loading = false;
      state.signIn.response = payload || null;
      state.authorized = !!(payload.access && payload.refresh);

      if (payload?.remember !== undefined) {
        state.remember = payload?.remember;
      }
    },
  },
});

export const { reducer, actions } = auth;

setActions(actions);

export function* saga(store: EnhancedStore) {
  yield spawn(authSaga, store);

  yield takeLatest(actions.OTPSuccess.toString(), function* trigger({ payload }: Action<OTPResponse>) {
    yield put({
      type: actions.signInSuccess.toString(),
      payload,
    });
  });

  // yield takeLatest(actions.userMeSuccess.toString(), function* trigger() {
  //   const authData = (yield getState()) as State;
  //
  //   if (authData && authData.user && authData.user.role === UserRoles.GUEST) {
  //     yield put(signOut());
  //
  //     window.location.href = '/sign-up';
  //   }
  // });
}

The following information describes how the auth process functioning. The general course of actions is similar for other entities.

Redux action

[nameEntity]            // Signals that the process has been called.
[nameEntity]Start       // With this event, all errors that could remain in the store are deleted and a flag is set to indicate the start of the request.
[nameEntity]Success     // Positive response from the server. Everything is fine.
[nameEntity]Error       // An error has occurred. We learn about the reason from the error field in the store.
[nameEntity]ErrorClear  // Automatically clear errors after a certain period of time. Default is 10 seconds.
[nameEntity]Clear       // Automatically clear data after a certain period of time. Default is 4 seconds.

Sign-in

// Sign-in. There are many entry options.
signIn
signInStart
signInSuccess
signInError
signInErrorClear
signInClear

List of action creators

signIn(({
  email: 'email@gmail.com',
  password: 'string'
});

signInByUsername({
  username: 'string',
  password: 'string'
});

signInByPhone({
  phone: '+380673456789',
  password: 'string' // optional
});

signInByService({
  //              google        facebook     apple
  accessToken: access_token | accessToken | id_token,
  method: 'facebook' | 'google' | 'apple' | string
})

signInByServiceError({
  message: 'some custom error'
})

OTP

// When registering or logging in using a phone number, we will receive a code of 4 or more digits. The number of digits is not configured here.
OTP
OTPStart
OTPSuccess
OTPError
OTPErrorClear
OTPClear
// Generate new code. Code has a lifetime. After a certain time it becomes invalid.
OTPGenerate 
OTPGenerateStart
OTPGenerateSuccess
OTPGenerateError
OTPGenerateErrorClear
OTPGenerateClear

List of action creators

OTP({
  secretCode: Object.values(values).join(''),
  secretKey: 'string'
});

OTPGenerate({
  secretKey: 'string'
});

OTPGenerate({
  secretKey: 'string',
  // If you set this flag, then the code will be sent to your email.
  useEmail: true
});

Confirm

// Confirm email. Instructions with a link will be sent by email.
confirm
confirmStart
confirmSuccess
confirmError
confirmErrorClear
confirmClear

List of action creators

confirmEmail({
  secretKey: 'string',
  password: 'string' // optional - if you created an account using the fast path you must set the password
});

User-me

// After successful authorization, obtain user data.
userMe
userMeStart
userMeSuccess
userMeError
userMeErrorClear
userMeClear

List of action creators

userMe(); // does not have parameters

Sign-up

// Sign-up. There are many entry options.
signUp
signUpStart
signUpSuccess
signUpError
signUpErrorClear
signUpClear

List of action creators

signUp({
  email: 'string',
  password: 'string' // optional - easy way.
  // In this case, you will receive an email with a link. By clicking on it you will need to create a password.
  ...any else values
});

signUpByUsername({
  username: 'string',
  password: 'string'
});

signUpByPhone({
  phone: 'string',
  password: 'string' // optional
  // If you do not create a password, then you will use the confirmation code that will be sent to your phone as a password when logging in.
});

signUpByService({
  //              google        facebook     apple
  accessToken: access_token | accessToken | id_token,
  method: 'facebook' | 'google' | 'apple' | string
});

signUpByServiceError({
  message: 'some custom error'
});

Sign-out

// Delete all user data. If you want to redirect to the main page after this, subscribe to the signOutSuccess event.
signOut
signOutStart
signOutSuccess
signOutError
signOutErrorClear
signOutClear

List of action creators

signOut(); // does not have parameters

Forgot password

// Instructions with a link will be sent by email.
forgotPassword
forgotPasswordStart
forgotPasswordSuccess
forgotPasswordError
forgotPasswordErrorClear
forgotPasswordClear

List of action creators

forgotPassword({
  email: string;
});

Reset password

// Follow the link in the email and enter a new password.
resetPassword
resetPasswordStart
resetPasswordSuccess
resetPasswordError
resetPasswordErrorClear
resetPasswordClear

List of action creators

resetPassword({
  password: string;
  secretKey: string;
});

Use the following reference example, in order to send language used by the user in every request

updateConfig({

  // override function behavior
  createUrl: (
    url: string | UrlObject,    // full path
    addToUrl?: string,          // additions can come from where the function is called
    payload?: PayloadAction,    // payload action creator
    data?: AnyObject,           // data parameter that is in action
    _query?: AnyObject,         // additional parameters // the lang parameter is part of the query parameters
  ) => createUrl(url, addToUrl, payload, data, {
    ..._query,
    lang: 'en', // here use your desired language
  }),

  // OR if you need to add in the path to the endpoint

  createUrl: (
    url: string | UrlObject,
    addToUrl?: string,
    payload?: PayloadAction,
    data?: AnyObject,
    _query?: AnyObject,
  ) => createUrl(url, '/en' + addToUrl, payload, data, _query),

});

Package Sidebar

Install

npm i @triare/auth-redux

Weekly Downloads

5

Version

0.2.9

License

MIT

Unpacked Size

264 kB

Total Files

58

Last publish

Collaborators

  • vladimir93