@connectid-tools/idp-selector-react 6.9.0 → 6.10.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CidButton.d.ts +6 -2
- package/CidButton.js +21 -3
- package/Popup.d.ts +2 -2
- package/Popup.js +1 -1
- package/README.md +26 -2
- package/certification.d.ts +2 -1
- package/certification.js +3 -0
- package/certification.test.js +31 -2
- package/compability.test.d.ts +1 -0
- package/compability.test.js +97 -0
- package/compatibility.d.ts +3 -0
- package/compatibility.js +36 -0
- package/package.json +6 -6
- package/types.d.ts +17 -2
- package/userAgent.d.ts +5 -0
- package/userAgent.js +20 -0
package/CidButton.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import './CidButton.css';
|
|
2
2
|
import './global.css';
|
|
3
|
-
import { CertificationStatus, CidButtonLabel, CidButtonSize, CidButtonTheme, CidButtonType, Claim, IntroTextType, LogoMode, RequiredCertification } from './types';
|
|
3
|
+
import { CertificationStatus, CidButtonLabel, CidButtonSize, CidButtonTheme, CidButtonType, Claim, CompatibilityWarnings, IntroTextType, LogoMode, RequiredCertification } from './types';
|
|
4
4
|
type CidButtonProps = {
|
|
5
|
-
onProceed: (authorisationServerId: string) => void;
|
|
5
|
+
onProceed: (authorisationServerId: string, compatibilityWarnings: CompatibilityWarnings) => void;
|
|
6
6
|
claims: Claim[];
|
|
7
7
|
requiredClaims?: Claim[];
|
|
8
8
|
rpLogoUrl?: string;
|
|
@@ -21,6 +21,10 @@ type CidButtonProps = {
|
|
|
21
21
|
logoLockupStyle?: LogoMode;
|
|
22
22
|
showInfoContent?: boolean;
|
|
23
23
|
onError?: (error: string) => void;
|
|
24
|
+
forceMobile?: boolean;
|
|
25
|
+
forceIos?: boolean;
|
|
26
|
+
forceAndroid?: boolean;
|
|
27
|
+
forceIncognito?: boolean;
|
|
24
28
|
};
|
|
25
29
|
export declare const CidButton: React.FC<CidButtonProps>;
|
|
26
30
|
export {};
|
package/CidButton.js
CHANGED
|
@@ -27,7 +27,14 @@ import './global.css';
|
|
|
27
27
|
import { getCurrentUrlOrigin } from './navigator';
|
|
28
28
|
import { isIntroTextTypeValid, isRequiredClaimsValid } from './validator';
|
|
29
29
|
import detectIncognito from './incognito';
|
|
30
|
-
|
|
30
|
+
import { getDeviceType, isAndroid, isChromeBrowser, isIos, isSamsungBrowser } from './userAgent';
|
|
31
|
+
import { hasCertification } from './certification';
|
|
32
|
+
import { getCompatibility } from './compatibility';
|
|
33
|
+
export const CidButton = ({ onProceed, claims, requiredClaims, rpLogoUrl, requiredCertifications, label = 'Verify', size = 'regular', type, introTextType, fullProcessCollapsible = false, participantsUri = 'https://data.directory.connectid.com.au/participants', certificationStatus = 'Active', rpDataSharingDescription = 'Your details will be shared with the current website.', enableManualVerification = false, showAllParticipants = false, theme = 'dark', logoLockupStyle = 'light', showInfoContent = false, onError = () => { }, forceMobile = false, // only for testing purposes
|
|
34
|
+
forceIos = false, // only for testing purposes
|
|
35
|
+
forceAndroid = false, // only for testing purposes
|
|
36
|
+
forceIncognito = false, // only for testing purposes
|
|
37
|
+
}) => {
|
|
31
38
|
const { data: participants, error, isLoading, } = useSWR(participantsUri, () => __awaiter(void 0, void 0, void 0, function* () {
|
|
32
39
|
const response = yield fetch(participantsUri);
|
|
33
40
|
if (!response.ok)
|
|
@@ -112,10 +119,21 @@ export const CidButton = ({ onProceed, claims, requiredClaims, rpLogoUrl, requir
|
|
|
112
119
|
});
|
|
113
120
|
track();
|
|
114
121
|
}, []);
|
|
115
|
-
const handleOnProceed = (authorisationServerId, authorisationServerName) => {
|
|
122
|
+
const handleOnProceed = (authorisationServerId, authorisationServerName, authorisationServerCertifications) => {
|
|
116
123
|
analytics === null || analytics === void 0 ? void 0 : analytics.track('IDP Selected', Object.assign(Object.assign({}, analyticsData), { authorisationServerId, idp_name: authorisationServerName, incognito: isIncognito }));
|
|
117
124
|
setShowPopup(false);
|
|
118
|
-
|
|
125
|
+
const isMobile = forceMobile || getDeviceType() === 'mobile';
|
|
126
|
+
const idpHasAppCertification = hasCertification(authorisationServerCertifications, 'Channel', 'app');
|
|
127
|
+
const compatibility = getCompatibility({
|
|
128
|
+
idpHasAppCertification,
|
|
129
|
+
isMobile,
|
|
130
|
+
isIncognito: forceIncognito || !!isIncognito,
|
|
131
|
+
isAndroid: forceAndroid || isAndroid(),
|
|
132
|
+
isIos: forceIos || isIos(),
|
|
133
|
+
isSamsungBrowser: isSamsungBrowser(),
|
|
134
|
+
isChromeBrowser: isChromeBrowser(),
|
|
135
|
+
});
|
|
136
|
+
onProceed(authorisationServerId, compatibility);
|
|
119
137
|
};
|
|
120
138
|
const handleButtonClick = () => {
|
|
121
139
|
setShowPopup(true);
|
package/Popup.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import './global.css';
|
|
2
2
|
import './Popup.css';
|
|
3
|
-
import { CertificationStatus, Claim, Participant, RequiredCertification } from './types';
|
|
3
|
+
import { AuthorisationServerCertification, CertificationStatus, Claim, Participant, RequiredCertification } from './types';
|
|
4
4
|
type PopupProps = {
|
|
5
|
-
onProceed: (authorisationServerId: string, authorisationServerName: string) => void;
|
|
5
|
+
onProceed: (authorisationServerId: string, authorisationServerName: string, authorisationServerCertifications: AuthorisationServerCertification[]) => void;
|
|
6
6
|
onCancel: () => void;
|
|
7
7
|
onError?: (error: string) => void;
|
|
8
8
|
claims: Claim[];
|
package/Popup.js
CHANGED
|
@@ -145,7 +145,7 @@ export const Popup = ({ onProceed, onCancel, onError, claims, requiredClaims, sh
|
|
|
145
145
|
});
|
|
146
146
|
const missingAuthorisationServerWithProfileRedirectFapi2 = authorisationServerWithProfileRedirectFapi2.length !== filteredAuthorisationServers.length;
|
|
147
147
|
const hasManualIdp = enableManualVerification && !!manualAuthorisationServer;
|
|
148
|
-
return (_jsxs(_Fragment, { children: [filteredAuthorisationServers.length > 0 ? (_jsxs("div", { className: "cid-idp-selector-popup-provider-wrapper", children: [_jsx("h3", { className: "cid-idp-selector-popup-subtitle", children: "Choose a provider" }), _jsx("ul", { className: "cid-idp-selector-popup-participants", "data-testid": "participants", children: filteredAuthorisationServers.map(({ AuthorisationServerId, CustomerFriendlyName, CustomerFriendlyLogoUri }) => (_jsxs("li", { "data-testid": "participant", className: "cid-idp-selector-popup-participant", tabIndex: 0, onClick: () => onProceed(AuthorisationServerId, CustomerFriendlyName), children: [_jsx("img", { className: "cid-idp-selector-popup-participant-icon", src: CustomerFriendlyLogoUri, alt: `${CustomerFriendlyName} logo` }), _jsx("div", { className: "cid-idp-selector-popup-participant-name", title: CustomerFriendlyName, "data-testid": "participant-name", children: CustomerFriendlyName })] }, AuthorisationServerId))) }), missingAuthorisationServerWithProfileRedirectFapi2 && (_jsxs("div", { className: "cid-idp-selector-popup-missing", "data-testid": "missing-auth-server", children: [_jsx("p", { className: "cid-idp-selector-popup-missing-title", children: "Can't find your provider?" }), _jsx("p", { className: "cid-idp-selector-popup-missing-text", children: "Not all of our identity providers are available for this verification." })] }))] })) : (_jsx("div", { className: "cid-idp-selector-popup-info-box-error-wrapper", "data-testid": "popup-error-info-box", children: _jsx(NoIdpError, { hasManualIdp: hasManualIdp }) })), enableManualVerification && manualAuthorisationServer && (_jsx(_Fragment, { children: _jsxs("p", { className: "cid-idp-selector-popup-manual", children: [_jsx("span", { className: "cid-idp-selector-popup-manual-title", "data-testid": "manual-verification-text", children: "Or, use a document to verify your identity" }), _jsx("span", { className: "cid-idp-selector-popup-manual-action", tabIndex: 0, onClick: () => onProceed(manualAuthorisationServer.AuthorisationServerId, manualAuthorisationServer.CustomerFriendlyName), "data-testid": "manual-verification-action", role: "link", children: "Verify with a document" }), _jsx("span", { className: "cid-idp-selector-popup-manual-description", children: "ConnectID does not collect, hold or store your personal information at any time." })] }) }))] }));
|
|
148
|
+
return (_jsxs(_Fragment, { children: [filteredAuthorisationServers.length > 0 ? (_jsxs("div", { className: "cid-idp-selector-popup-provider-wrapper", children: [_jsx("h3", { className: "cid-idp-selector-popup-subtitle", children: "Choose a provider" }), _jsx("ul", { className: "cid-idp-selector-popup-participants", "data-testid": "participants", children: filteredAuthorisationServers.map(({ AuthorisationServerId, CustomerFriendlyName, CustomerFriendlyLogoUri, AuthorisationServerCertifications }) => (_jsxs("li", { "data-testid": "participant", className: "cid-idp-selector-popup-participant", tabIndex: 0, onClick: () => onProceed(AuthorisationServerId, CustomerFriendlyName, AuthorisationServerCertifications), children: [_jsx("img", { className: "cid-idp-selector-popup-participant-icon", src: CustomerFriendlyLogoUri, alt: `${CustomerFriendlyName} logo` }), _jsx("div", { className: "cid-idp-selector-popup-participant-name", title: CustomerFriendlyName, "data-testid": "participant-name", children: CustomerFriendlyName })] }, AuthorisationServerId))) }), missingAuthorisationServerWithProfileRedirectFapi2 && (_jsxs("div", { className: "cid-idp-selector-popup-missing", "data-testid": "missing-auth-server", children: [_jsx("p", { className: "cid-idp-selector-popup-missing-title", children: "Can't find your provider?" }), _jsx("p", { className: "cid-idp-selector-popup-missing-text", children: "Not all of our identity providers are available for this verification." })] }))] })) : (_jsx("div", { className: "cid-idp-selector-popup-info-box-error-wrapper", "data-testid": "popup-error-info-box", children: _jsx(NoIdpError, { hasManualIdp: hasManualIdp }) })), enableManualVerification && manualAuthorisationServer && (_jsx(_Fragment, { children: _jsxs("p", { className: "cid-idp-selector-popup-manual", children: [_jsx("span", { className: "cid-idp-selector-popup-manual-title", "data-testid": "manual-verification-text", children: "Or, use a document to verify your identity" }), _jsx("span", { className: "cid-idp-selector-popup-manual-action", tabIndex: 0, onClick: () => onProceed(manualAuthorisationServer.AuthorisationServerId, manualAuthorisationServer.CustomerFriendlyName, manualAuthorisationServer.AuthorisationServerCertifications), "data-testid": "manual-verification-action", role: "link", children: "Verify with a document" }), _jsx("span", { className: "cid-idp-selector-popup-manual-description", children: "ConnectID does not collect, hold or store your personal information at any time." })] }) }))] }));
|
|
149
149
|
};
|
|
150
150
|
return ReactDOM.createPortal(_jsxs("div", { className: "cid-idp-selector-popup", tabIndex: 0, ref: popupRef, onKeyDown: handleEnterEsc, children: [_jsxs("div", { className: "cid-idp-selector-popup-logo-container", children: [_jsxs("div", { className: "cid-idp-selector-popup-logos", children: [_jsxs("svg", { role: "img", focusable: "false", className: "cid-idp-selector-popup-logo", viewBox: "0 0 132.37 157.84", "data-testid": "cid-logo", children: [_jsx("title", { children: "ConnectID logo" }), _jsx("path", { d: "M102.22,91.65c0,19.9-16.13,36.03-36.03,36.03s-36.03-16.13-36.03-36.03,16.13-36.03,36.03-36.03c9.95,0,18.96,4.03,25.48,10.55l40.71-40.71c-6.52-6.52-10.55-15.52-10.55-25.46h-30.14c0,13.45,4.02,25.95,10.92,36.39-10.45-6.9-22.96-10.93-36.42-10.93C29.63,25.47,0,55.1,0,91.65s29.63,66.19,66.19,66.19,66.19-29.63,66.19-66.19h-30.16Z" })] }), rpLogoUrl && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "cid-idp-selector-popup-logo-dots", children: [_jsx("div", { className: "cid-idp-selector-popup-logo-dot" }), _jsx("div", { className: "cid-idp-selector-popup-logo-dot" }), _jsx("div", { className: "cid-idp-selector-popup-logo-dot" })] }), _jsx("img", { className: "cid-idp-selector-popup-logo-rp", src: rpLogoUrl, alt: "RP logo", "data-testid": "rp-logo" })] }))] }), _jsxs("button", { "aria-label": "Close popup", "data-testid": "popup-close", className: "cid-idp-selector-popup-close", onClick: onCancel, tabIndex: 0, ref: closeButtonRef, children: [_jsx("span", { className: "cid-idp-selector-popup-close-label", children: "Close" }), _jsx("svg", { clipRule: "evenodd", fillRule: "evenodd", strokeLinejoin: "round", strokeMiterlimit: "2", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "m12 10.93 5.719-5.72c.146-.146.339-.219.531-.219.404 0 .75.324.75.749 0 .193-.073.385-.219.532l-5.72 5.719 5.719 5.719c.147.147.22.339.22.531 0 .427-.349.75-.75.75-.192 0-.385-.073-.531-.219l-5.719-5.719-5.719 5.719c-.146.146-.339.219-.531.219-.401 0-.75-.323-.75-.75 0-.192.073-.384.22-.531l5.719-5.719-5.72-5.719c-.146-.147-.219-.339-.219-.532 0-.425.346-.749.75-.749.192 0 .385.073.531.219z" }) })] })] }), _jsxs("div", { className: "cid-idp-selector-popup-title-description-container", children: [_jsx("h2", { className: "cid-idp-selector-popup-title", children: popupTitle }), _jsx("p", { className: "cid-idp-selector-popup-description", children: rpDataSharingDescription })] }), renderClaims(), renderAuthorisationServers(), _jsx("div", { className: "cid-idp-selector-popup-info-box-wrapper", children: _jsxs(InfoBox, { children: [_jsx("h4", { className: "cid-idp-selector-popup-info-box-title", children: "What is ConnectID?" }), _jsxs("p", { className: "cid-idp-selector-popup-info-box-text", "data-testid": "whats-cid-text", children: ["ConnectID is an Australian-owned digital identity solution which allows you to securely prove who you are. ConnectID does not see or store your personal data. Visit", ' ', _jsx("a", { className: "cid-idp-selector-popup-info-box-link", href: "https://www.connectid.com.au", onClick: handleConnectIdLinkClicked, target: "_blank", tabIndex: 0, "data-testid": "info-box-link", ref: infoboxLinkRef, children: "connectid.com.au" }), ' ', "to find out more."] })] }) })] }), document.querySelector('body'));
|
|
151
151
|
};
|
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Place the `<CidButton>` component where you want to render the ConnectID button
|
|
|
14
14
|
import { CidButton } from '@connectid-tools/idp-selector-react'
|
|
15
15
|
|
|
16
16
|
<CidButton
|
|
17
|
-
onProceed={(authorisationServerClicked: string) => {
|
|
17
|
+
onProceed={(authorisationServerClicked: string, compatibilityWarnings: CompatibilityWarnings) => {
|
|
18
18
|
/* do something here */
|
|
19
19
|
}}
|
|
20
20
|
claims={['email']}
|
|
@@ -31,7 +31,7 @@ Your page should render a widget similar to this 👇 And when the button is cli
|
|
|
31
31
|
|
|
32
32
|
| key | description | type | required | default |
|
|
33
33
|
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------------- |
|
|
34
|
-
| onProceed | Callback to be called when IDP Selector `Proceed` button is clicked | (authorisationServerId: string) => void | yes | - |
|
|
34
|
+
| onProceed | Callback to be called when IDP Selector `Proceed` button is clicked | (authorisationServerId: string, compatibilityWarnings: CompatibilityWarnings) => void | yes | - |
|
|
35
35
|
| claims | List of claims to be displayed to the user and will be requested from IDP to be shared. If `requiredClaims` not supplied then only IDPs that support all listed `claims` will be displayed to the user. | Claim[] | yes | - |
|
|
36
36
|
| requiredClaims | List of claims to be supported by IDPs. Must be a subset of `claims`. Where an RP can successfully complete their use case without all claims specified in `claims`, they can specify `requiredClaims` with the minimum list of claims needed, which will then be used for filtering and may result in additional IDP options being displayed to the customer. | Claim[] | no (`claims` will be used if no `requiredClaims`) | - |
|
|
37
37
|
| label | The button label. Do not use 'Sign Up'. It is deprecated. | 'Verify' \| 'Login' \| 'Sign Up' \| 'Age check' \| 'Sign up' \| 'Authenticate' \| 'Checkout' | no | 'Verify' |
|
|
@@ -51,6 +51,24 @@ Your page should render a widget similar to this 👇 And when the button is cli
|
|
|
51
51
|
| showInfoContent | The text that goes below the ConnectID button [More details here](#infoContent) | boolean | false | false |
|
|
52
52
|
| onError | Callback to be called when there is an error fetching participants or invalid options. | () => void | no | () => {} // do nothing |
|
|
53
53
|
|
|
54
|
+
#### CompatibilityWarnings type
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
export type CompatibilityWarnings = {
|
|
58
|
+
mightFail: boolean
|
|
59
|
+
isMobile: boolean
|
|
60
|
+
isIncognito: boolean
|
|
61
|
+
mobileOs: 'android' | 'ios' | 'other'
|
|
62
|
+
browser: 'chrome' | 'samsung' | 'safari' | 'firefox' | 'other'
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
It returns information about the compatibility of the user's browser and device with the ConnectID flow. It includes:
|
|
66
|
+
- `mightFail`: Indicates if the flow might fail due to compatibility issues.
|
|
67
|
+
- `isMobile`: Indicates if the user is on a mobile device.
|
|
68
|
+
- `isIncognito`: Indicates if the user is browsing in incognito mode.
|
|
69
|
+
- `mobileOs`: The operating system of the mobile device, if applicable.
|
|
70
|
+
- `browser`: The browser being used by the user.
|
|
71
|
+
|
|
54
72
|
#### certificationStatus values
|
|
55
73
|
|
|
56
74
|
- `Active`: only IDPs with active certification
|
|
@@ -223,6 +241,12 @@ The SVG image below the ConnectID button has either option dark or light mode. B
|
|
|
223
241
|
|
|
224
242
|
## 🚀 Release Notes
|
|
225
243
|
|
|
244
|
+
### 6.10.0-alpha.1 (Jun 12, 2025)
|
|
245
|
+
- Rename `compabilityWarnings` to `compatibilityWarnings`.
|
|
246
|
+
|
|
247
|
+
### 6.10.0-alpha.0 (Jun 10, 2025)
|
|
248
|
+
- Add `compabilityWarnings` to `onProceed` callback.
|
|
249
|
+
|
|
226
250
|
### 6.9.0 (Jun 5, 2025)
|
|
227
251
|
- Add new `introTextType` option: `verify-age`.
|
|
228
252
|
|
package/certification.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { AuthorisationServer, CertificationStatus, Claim, Device, RequiredCertification } from './types';
|
|
1
|
+
import { AuthorisationServer, AuthorisationServerCertification, CertificationProfileType, CertificationProfileVariant, CertificationStatus, Claim, Device, RequiredCertification } from './types';
|
|
2
2
|
export declare const filterAuthorisationServers: (authorisationServers: AuthorisationServer[], showAllParticipants: boolean, certificationStatus: CertificationStatus, claims: Claim[], requiredCertifications?: RequiredCertification[]) => AuthorisationServer[];
|
|
3
3
|
export declare const getManualAuthorisationServer: (authorisationServers: AuthorisationServer[], showAllParticipants: boolean) => AuthorisationServer | undefined;
|
|
4
4
|
export declare const removeExpiredOrNotStartedCertifications: (authorisationServers: AuthorisationServer[]) => AuthorisationServer[];
|
|
5
5
|
export declare const filterAuthorisationServerByClaims: (authorisationServers: AuthorisationServer[], claims: Claim[]) => AuthorisationServer[];
|
|
6
6
|
export declare const filterAuthorisationServerByCertification: (authorisationServers: AuthorisationServer[], status: CertificationStatus, requiredCertifications?: RequiredCertification[]) => AuthorisationServer[];
|
|
7
7
|
export declare const filterAuthorisationServerByDevice: (authorisationServers: AuthorisationServer[], deviceType: Device) => AuthorisationServer[];
|
|
8
|
+
export declare const hasCertification: (certifications: AuthorisationServerCertification[], certificationType: CertificationProfileType, certificationVariant: CertificationProfileVariant) => boolean;
|
|
8
9
|
export declare const hasStarted: (startDateStr: string) => boolean;
|
package/certification.js
CHANGED
|
@@ -82,6 +82,9 @@ export const filterAuthorisationServerByDevice = (authorisationServers, deviceTy
|
|
|
82
82
|
const onlyAppAuthorisationServersIds = onlyAppAuthorisationServers.map((authorisationServer) => authorisationServer.AuthorisationServerId);
|
|
83
83
|
return authorisationServers.filter((authorisationServer) => !onlyAppAuthorisationServersIds.includes(authorisationServer.AuthorisationServerId));
|
|
84
84
|
};
|
|
85
|
+
export const hasCertification = (certifications, certificationType, certificationVariant) => {
|
|
86
|
+
return certifications.some(({ ProfileType, ProfileVariant }) => ProfileType === certificationType && ProfileVariant === certificationVariant);
|
|
87
|
+
};
|
|
85
88
|
const isExpired = (expiryDateStr) => {
|
|
86
89
|
const expiryDate = parseCertificationDate(expiryDateStr);
|
|
87
90
|
expiryDate.setHours(23);
|
package/certification.test.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
2
|
import { describe, it } from 'node:test';
|
|
3
|
-
import { filterAuthorisationServerByClaims, filterAuthorisationServerByDevice, filterAuthorisationServers, getManualAuthorisationServer, removeExpiredOrNotStartedCertifications, } from './certification';
|
|
3
|
+
import { filterAuthorisationServerByClaims, filterAuthorisationServerByDevice, filterAuthorisationServers, getManualAuthorisationServer, hasCertification, removeExpiredOrNotStartedCertifications, } from './certification';
|
|
4
4
|
describe('filterAuthorisationServers', () => {
|
|
5
5
|
it('should return all authorisation servers if showAllParticipants is true regardless of certification status, required certifications and claims', () => {
|
|
6
6
|
const authorisationServers = [
|
|
@@ -393,6 +393,35 @@ describe('filterAuthorisationServerByDevice', () => {
|
|
|
393
393
|
],
|
|
394
394
|
},
|
|
395
395
|
];
|
|
396
|
-
assert.deepStrictEqual(filterAuthorisationServerByDevice(authorisationServers, 'desktop'), [
|
|
396
|
+
assert.deepStrictEqual(filterAuthorisationServerByDevice(authorisationServers, 'desktop'), [
|
|
397
|
+
authorisationServers[1],
|
|
398
|
+
authorisationServers[2],
|
|
399
|
+
]);
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
describe('hasCertification', () => {
|
|
403
|
+
it('should return true if certifications have channel app', () => {
|
|
404
|
+
const certifications = [
|
|
405
|
+
{
|
|
406
|
+
CertificationStartDate: '10/04/1900',
|
|
407
|
+
CertificationExpirationDate: '10/04/2099',
|
|
408
|
+
Status: 'Certified',
|
|
409
|
+
ProfileVariant: 'app',
|
|
410
|
+
ProfileType: 'Channel',
|
|
411
|
+
},
|
|
412
|
+
];
|
|
413
|
+
assert.ok(hasCertification(certifications, 'Channel', 'app') === true);
|
|
414
|
+
});
|
|
415
|
+
it('should return false if certifications do not have channel app', () => {
|
|
416
|
+
const certifications = [
|
|
417
|
+
{
|
|
418
|
+
CertificationStartDate: '10/04/1900',
|
|
419
|
+
CertificationExpirationDate: '10/04/2099',
|
|
420
|
+
Status: 'Certified',
|
|
421
|
+
ProfileVariant: 'web',
|
|
422
|
+
ProfileType: 'Channel',
|
|
423
|
+
},
|
|
424
|
+
];
|
|
425
|
+
assert.ok(hasCertification(certifications, 'Channel', 'app') === false);
|
|
397
426
|
});
|
|
398
427
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { getCompatibility, getMightFail } from './compatibility';
|
|
4
|
+
describe('getCompatibility', () => {
|
|
5
|
+
it('should return compatibility warnings', () => {
|
|
6
|
+
const compatibility = getCompatibility({
|
|
7
|
+
idpHasAppCertification: true,
|
|
8
|
+
isMobile: true,
|
|
9
|
+
isIncognito: false,
|
|
10
|
+
isAndroid: false,
|
|
11
|
+
isIos: false,
|
|
12
|
+
isSamsungBrowser: false,
|
|
13
|
+
isChromeBrowser: false,
|
|
14
|
+
});
|
|
15
|
+
assert.deepStrictEqual(compatibility, {
|
|
16
|
+
isMobile: true,
|
|
17
|
+
isIncognito: false,
|
|
18
|
+
mobileOs: 'other',
|
|
19
|
+
browser: 'other',
|
|
20
|
+
mightFail: false
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
describe('getMightFail', () => {
|
|
25
|
+
it('should return false when idpHasAppCertification is false', () => {
|
|
26
|
+
const mightFail = getMightFail({
|
|
27
|
+
idpHasAppCertification: false,
|
|
28
|
+
isMobile: true,
|
|
29
|
+
isIncognito: false,
|
|
30
|
+
isAndroid: false,
|
|
31
|
+
isIos: false,
|
|
32
|
+
isSamsungBrowser: false,
|
|
33
|
+
isChromeBrowser: false,
|
|
34
|
+
});
|
|
35
|
+
assert.ok(mightFail === false);
|
|
36
|
+
});
|
|
37
|
+
it('should return false when isMobile is false', () => {
|
|
38
|
+
const mightFail = getMightFail({
|
|
39
|
+
idpHasAppCertification: true,
|
|
40
|
+
isMobile: false,
|
|
41
|
+
isIncognito: false,
|
|
42
|
+
isAndroid: false,
|
|
43
|
+
isIos: false,
|
|
44
|
+
isSamsungBrowser: false,
|
|
45
|
+
isChromeBrowser: false,
|
|
46
|
+
});
|
|
47
|
+
assert.ok(mightFail === false);
|
|
48
|
+
});
|
|
49
|
+
it('should return true when isIncognito is true, isAndroid is true, and isChromeBrowser is true', () => {
|
|
50
|
+
const mightFail = getMightFail({
|
|
51
|
+
idpHasAppCertification: true,
|
|
52
|
+
isMobile: true,
|
|
53
|
+
isIncognito: true,
|
|
54
|
+
isAndroid: true,
|
|
55
|
+
isIos: false,
|
|
56
|
+
isSamsungBrowser: false,
|
|
57
|
+
isChromeBrowser: true,
|
|
58
|
+
});
|
|
59
|
+
assert.ok(mightFail === true);
|
|
60
|
+
});
|
|
61
|
+
it('should return true when isAndroid is true and isSamsungBrowser is true', () => {
|
|
62
|
+
const mightFail = getMightFail({
|
|
63
|
+
idpHasAppCertification: true,
|
|
64
|
+
isMobile: true,
|
|
65
|
+
isIncognito: false,
|
|
66
|
+
isAndroid: true,
|
|
67
|
+
isIos: false,
|
|
68
|
+
isSamsungBrowser: true,
|
|
69
|
+
isChromeBrowser: false,
|
|
70
|
+
});
|
|
71
|
+
assert.ok(mightFail === true);
|
|
72
|
+
});
|
|
73
|
+
it('should return true when isIncognito is true and isIos is true', () => {
|
|
74
|
+
const mightFail = getMightFail({
|
|
75
|
+
idpHasAppCertification: true,
|
|
76
|
+
isMobile: true,
|
|
77
|
+
isIncognito: true,
|
|
78
|
+
isAndroid: false,
|
|
79
|
+
isIos: true,
|
|
80
|
+
isSamsungBrowser: false,
|
|
81
|
+
isChromeBrowser: false,
|
|
82
|
+
});
|
|
83
|
+
assert.ok(mightFail === true);
|
|
84
|
+
});
|
|
85
|
+
it('should return false when none of the conditions are met', () => {
|
|
86
|
+
const mightFail = getMightFail({
|
|
87
|
+
idpHasAppCertification: true,
|
|
88
|
+
isMobile: true,
|
|
89
|
+
isIncognito: false,
|
|
90
|
+
isAndroid: false,
|
|
91
|
+
isIos: false,
|
|
92
|
+
isSamsungBrowser: false,
|
|
93
|
+
isChromeBrowser: false,
|
|
94
|
+
});
|
|
95
|
+
assert.ok(mightFail === false);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { CompatibilityEnvironment, CompatibilityWarnings } from './types';
|
|
2
|
+
export declare const getCompatibility: (env: CompatibilityEnvironment) => CompatibilityWarnings;
|
|
3
|
+
export declare const getMightFail: (env: Pick<CompatibilityEnvironment, "idpHasAppCertification" | "isMobile" | "isIncognito" | "isAndroid" | "isIos" | "isSamsungBrowser" | "isChromeBrowser">) => boolean;
|
package/compatibility.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getBrowserName } from './userAgent';
|
|
2
|
+
export const getCompatibility = (env) => {
|
|
3
|
+
const { idpHasAppCertification, isAndroid, isChromeBrowser, isIncognito, isMobile, isSamsungBrowser, isIos } = env;
|
|
4
|
+
const mightFail = getMightFail({
|
|
5
|
+
idpHasAppCertification,
|
|
6
|
+
isMobile,
|
|
7
|
+
isIncognito,
|
|
8
|
+
isAndroid,
|
|
9
|
+
isIos,
|
|
10
|
+
isSamsungBrowser,
|
|
11
|
+
isChromeBrowser,
|
|
12
|
+
});
|
|
13
|
+
return {
|
|
14
|
+
isMobile,
|
|
15
|
+
isIncognito,
|
|
16
|
+
mobileOs: isAndroid ? 'android' : isIos ? 'ios' : 'other',
|
|
17
|
+
browser: getBrowserName(),
|
|
18
|
+
mightFail,
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export const getMightFail = (env) => {
|
|
22
|
+
const { idpHasAppCertification, isMobile, isIncognito, isAndroid, isChromeBrowser, isSamsungBrowser, isIos } = env;
|
|
23
|
+
if (!idpHasAppCertification || !isMobile) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
if (isIncognito && isAndroid && isChromeBrowser) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
if (isAndroid && isSamsungBrowser) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
if (isIncognito && isIos) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@connectid-tools/idp-selector-react",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.10.0-alpha.1",
|
|
4
4
|
"description": "React component for IDP Selector.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -26,21 +26,21 @@
|
|
|
26
26
|
"homepage": "https://github.com/connectid-tools/idp-selector-react",
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@babel/preset-typescript": "^7.27.1",
|
|
29
|
-
"@types/node": "^22.15.
|
|
29
|
+
"@types/node": "^22.15.31",
|
|
30
30
|
"@types/react": "^18.3.23",
|
|
31
31
|
"@types/react-dom": "^18.3.7",
|
|
32
|
-
"cypress": "^
|
|
32
|
+
"cypress": "^14.4.1",
|
|
33
33
|
"jest-environment-jsdom": "^29.7.0",
|
|
34
34
|
"prettier": "^3.5.3",
|
|
35
35
|
"react": "^18.3.1",
|
|
36
36
|
"react-dom": "^18.3.1",
|
|
37
37
|
"replace-in-file": "^8.3.0",
|
|
38
38
|
"tsx": "^4.19.4",
|
|
39
|
-
"typescript": "^5.8.3"
|
|
40
|
-
"vite": "^5.4.19"
|
|
39
|
+
"typescript": "^5.8.3"
|
|
41
40
|
},
|
|
42
41
|
"dependencies": {
|
|
43
42
|
"react-responsive": "^10.0.1",
|
|
44
|
-
"swr": "^2.3.3"
|
|
43
|
+
"swr": "^2.3.3",
|
|
44
|
+
"vite": "^6.3.5"
|
|
45
45
|
}
|
|
46
46
|
}
|
package/types.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export type CertificationProfileType = 'TDIF Accreditation' | 'Redirect' | 'Chan
|
|
|
5
5
|
export type CertificationProfileVariant = 'FAPI2 Adv. OP w/Private Key, PAR' | 'Identity Provider' | 'Fallback Identity Service Provider' | 'web' | 'app' | 'name' | Claim;
|
|
6
6
|
export type CertificationStatus = 'Active' | 'All';
|
|
7
7
|
export type RequiredCertification = 'TDIF.IDP' | 'Redirect.FAPI2';
|
|
8
|
-
type AuthorisationServerCertification = {
|
|
8
|
+
export type AuthorisationServerCertification = {
|
|
9
9
|
Status: 'Certified' | 'Self-Certified' | 'Awaiting Certification' | 'Deprecated' | 'Rejected';
|
|
10
10
|
ProfileType: CertificationProfileType;
|
|
11
11
|
ProfileVariant: CertificationProfileVariant;
|
|
@@ -38,4 +38,19 @@ export type CidButtonTypeConfig = {
|
|
|
38
38
|
hasHowItWorks: boolean;
|
|
39
39
|
hasLearnMore: boolean;
|
|
40
40
|
};
|
|
41
|
-
export {
|
|
41
|
+
export type CompatibilityEnvironment = {
|
|
42
|
+
isMobile: boolean;
|
|
43
|
+
isIncognito: boolean;
|
|
44
|
+
idpHasAppCertification: boolean;
|
|
45
|
+
isAndroid: boolean;
|
|
46
|
+
isIos: boolean;
|
|
47
|
+
isSamsungBrowser: boolean;
|
|
48
|
+
isChromeBrowser: boolean;
|
|
49
|
+
};
|
|
50
|
+
export type CompatibilityWarnings = {
|
|
51
|
+
mightFail: boolean;
|
|
52
|
+
isMobile: boolean;
|
|
53
|
+
isIncognito: boolean;
|
|
54
|
+
mobileOs: 'android' | 'ios' | 'other';
|
|
55
|
+
browser: 'chrome' | 'samsung' | 'safari' | 'firefox' | 'other';
|
|
56
|
+
};
|
package/userAgent.d.ts
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
import { Device } from './types';
|
|
2
2
|
export declare const getDeviceType: () => Device;
|
|
3
|
+
export declare const isAndroid: () => boolean;
|
|
4
|
+
export declare const isIos: () => boolean;
|
|
5
|
+
export declare const isChromeBrowser: () => boolean;
|
|
6
|
+
export declare const isSamsungBrowser: () => boolean;
|
|
7
|
+
export declare const getBrowserName: () => "other" | "chrome" | "samsung" | "safari" | "firefox";
|
package/userAgent.js
CHANGED
|
@@ -8,3 +8,23 @@ export const getDeviceType = () => {
|
|
|
8
8
|
}
|
|
9
9
|
return 'desktop';
|
|
10
10
|
};
|
|
11
|
+
export const isAndroid = () => /Android/i.test(navigator.userAgent);
|
|
12
|
+
export const isIos = () => /iPhone|iPad|iPod/i.test(navigator.userAgent);
|
|
13
|
+
export const isChromeBrowser = () => /Chrome/.test(navigator.userAgent) && !/Edg|OPR|Brave/.test(navigator.userAgent);
|
|
14
|
+
export const isSamsungBrowser = () => /SamsungBrowser/.test(navigator.userAgent);
|
|
15
|
+
export const getBrowserName = () => {
|
|
16
|
+
const ua = navigator.userAgent.toLowerCase();
|
|
17
|
+
if (ua.includes('samsungbrowser')) {
|
|
18
|
+
return 'samsung';
|
|
19
|
+
}
|
|
20
|
+
if (ua.includes('firefox')) {
|
|
21
|
+
return 'firefox';
|
|
22
|
+
}
|
|
23
|
+
if (ua.includes('safari') && !ua.includes('chrome')) {
|
|
24
|
+
return 'safari';
|
|
25
|
+
}
|
|
26
|
+
if (ua.includes('chrome') || ua.includes('crios')) {
|
|
27
|
+
return 'chrome';
|
|
28
|
+
}
|
|
29
|
+
return 'other';
|
|
30
|
+
};
|