@insinet/otp-sso-auth
v1.0.10
Published
React OTP confirmation modal for internal SSO flows. Ships ESM + CJS + TypeScript types and scoped standalone CSS — the host app does not need Tailwind.
Readme
@insinet/otp-sso-auth
React OTP confirmation modal for internal SSO flows. Ships ESM + CJS + TypeScript types and scoped standalone CSS — the host app does not need Tailwind.
Install
yarn add @insinet/otp-sso-auth
# or: npm i @insinet/otp-sso-authPeer dependencies: react and react-dom (>=18.2).
Usage
import { useState } from 'react';
import { OTPConfirmationModal, Application, Environment } from '@insinet/otp-sso-auth';
export const Example = ({ requestId }: { requestId: string }) => {
const [open, setOpen] = useState(false);
return (
<>
<button type='button' onClick={() => setOpen(true)}>
Confirm with OTP
</button>
<OTPConfirmationModal
open={open}
onClose={() => setOpen(false)}
application={Application.CorporateOsVCFC}
environment={Environment.Production}
spinner={<div>Loading…</div>}
data={{ 'request-id': requestId, email: '[email protected]' }}
onSuccess={(data) => {
console.log(data.result.accessToken);
setOpen(false);
}}
onRevoke={() => {
// verify returned 404 — the request is no longer valid
setOpen(false);
}}
onError={(error) => {
console.error(error.status, error.message);
}}
/>
</>
);
};Props
| Prop | Type | Required | Description |
| ------------- | -------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| open | boolean | yes | Controls modal visibility. When true, loads OTP methods, picks the initial method, and sends one code. A new operationId is created for this opening. |
| onClose | () => void | yes | Called on Cancel or close. |
| application | Application | yes | Brand theme: Application.CorporateOs, Application.CorporateOsVCFC or Application.PersonalOsVCFC. Also selects the API host together with environment. |
| environment | Environment | no | API host: Environment.Development ('development') or Environment.Production ('production'). Defaults to Environment.Production. |
| spinner | ReactNode | no | Shown while OTP methods load. Rendered inside .otp-sso-auth-slot (not styled by the package reset). |
| data | { 'request-id': string } & Record<string, string> | yes | Payload for the send request. request-id is required; other string fields (e.g. email) are forwarded as-is. |
| onError | (error: RestApiError) => void | no | Called on API failures (status, message, data, method, baseUrl, url). Includes verify 400 and 404. |
| onRevoke | (error: RestApiError) => void | no | Called only when verify returns 404. Use it to leave the flow in the host app. onError is still called for the same response. |
| onSuccess | (data: { params: VerifyRequest; result: VerifySuccessResponse }) => void | no | Called when verify returns a valid code. result has accessToken, tokenType and expiresIn. params is the verify request. |
OTP session
Opening the modal creates one operationId. Every POST /otp/confirmation/send and POST /otp/confirmation/verify in that opening uses it. The next opening gets a new id.
GET /otp/confirmation/types is expected to return:
{
otpTypes: ('Totp' | 'Email' | 'Phone')[];
loginOtpType?: 'Totp' | 'Email' | 'Phone';
}The initial method is loginOtpType when it is present in otpTypes. Otherwise it is Totp when that type is in the list, otherwise the first item. The modal then calls send once for that method.
| Action | Request |
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| Switch to Email or Phone | send once, so the backend can deliver a code. |
| Switch to Authenticator (Totp) | send once per opening. Later selections do not send again. If that send fails, choosing Authenticator again sends once more. |
| Click the method that is already selected | No request. |
| Resend code | Email and Phone only. Same operationId, but not before 60 seconds have passed for that method. Authenticator has no resend control. |
| Return to Email or Phone after its 60-second countdown | send runs automatically. Authenticator does not send again. |
Email and Phone each keep their own countdown. It continues while another method is selected. Switching to Authenticator does not stop an SMS countdown. Coming back to SMS after that countdown reaches zero requests a new code. Coming back earlier does not. Coming back to Authenticator does not request another code after a successful send. After a failed send, choosing it again does.
Verify
| Response | What the modal does |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| Valid code | Calls onSuccess. |
| 400 and data.isValid === false | Clears the code and shows Invalid code, try again. The required-field message stays hidden until the next Confirm. Also calls onError. |
| 404 | Calls onRevoke and onError. |
| Other verify failure | Calls onError. |
Auth & theme (host contract)
The request base URL comes from application and environment:
| application | development | production |
| ----------------------------- | -------------------------- | --------------------------------- |
| Application.CorporateOs | https://sso.insidev.tech | https://auth.insignia-cards.com |
| Application.CorporateOsVCFC | https://sso.insidev.tech | https://auth.vcfc.eu |
| Application.PersonalOsVCFC | https://sso.insidev.tech | https://sso.insidev.tech |
Requests attach Authorization: Bearer <accessToken> when present.
The package reads host localStorage:
| Key | Expected value | Used for |
| -------- | ------------------------------------------------- | --------------------------------------------- |
| token | JSON { accessToken, expiresDate, refreshToken } | Bearer auth |
| isDark | "true" / other | Applies the dark class on the modal overlay |
Ensure token is set before opening the modal if the SSO endpoints require auth.
Styles
CSS is injected automatically with the main import (import './index.css' / require('./index.css') in the built entry).
Optional explicit import:
import '@insinet/otp-sso-auth/styles.css';Published CSS is standalone: no Tailwind required in the host, no @layer, selectors scoped to .otp-sso-auth. Host spinner content under .otp-sso-auth-slot is excluded from package reset styles. This is not Shadow DOM — host typography can still inherit.
Public exports
import {
OTPConfirmationModal,
Application,
Environment,
RestApiError,
type OTPConfirmationModalProps,
type RestApiErrorParams,
type VerifyRequest,
type VerifySuccessResponse,
} from '@insinet/otp-sso-auth';Local development
yarn install
yarn run dev # playground → http://localhost:8080
yarn run build # library (ESM + CJS + types + CSS)
yarn run build:playground # playground production build
yarn run preview # preview playground build
yarn run lint
yarn run format
yarn test # unit / regression tests
yarn check:package # pack → install into a temp consumer → buildprepack runs yarn build before pack/publish. yarn check:package needs registry access for consumer install and prints the retained artifact directory.
