@connectid-tools/idp-selector-react 6.8.0 → 6.10.0-alpha.0
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/IntroText.js +2 -1
- package/Popup.d.ts +2 -2
- package/Popup.js +1 -1
- package/README.md +28 -3
- package/certification.d.ts +2 -1
- package/certification.js +3 -0
- package/certification.test.js +31 -2
- package/compability.d.ts +3 -0
- package/compability.js +36 -0
- package/compability.test.d.ts +1 -0
- package/compability.test.js +97 -0
- package/package.json +7 -7
- package/types.d.ts +18 -3
- 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, CompabilityWarnings, IntroTextType, LogoMode, RequiredCertification } from './types';
|
|
4
4
|
type CidButtonProps = {
|
|
5
|
-
onProceed: (authorisationServerId: string) => void;
|
|
5
|
+
onProceed: (authorisationServerId: string, compabilityWarnings: CompabilityWarnings) => 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 { getCompability } from './compability';
|
|
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 compability = getCompability({
|
|
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, compability);
|
|
119
137
|
};
|
|
120
138
|
const handleButtonClick = () => {
|
|
121
139
|
setShowPopup(true);
|
package/IntroText.js
CHANGED
|
@@ -5,10 +5,11 @@ import { ThemeContext } from './ThemeContext';
|
|
|
5
5
|
const introTextContent = {
|
|
6
6
|
'verify-trust': 'ConnectID lets you verify your identity through details stored with an organisation you already trust, like your bank.',
|
|
7
7
|
'verify-account': 'Verify your identity through an organisation you already have an account with, like your bank.',
|
|
8
|
-
|
|
8
|
+
power: 'ConnectID gives you the power to choose how you share your details, using organisations you already trust.',
|
|
9
9
|
'sign-up': 'ConnectID lets you sign up using details stored with an organisation you already trust, like your bank.',
|
|
10
10
|
'bank-details-plus-personal-info': 'Provide your preferred bank account and personal details in a secure and simple way, without oversharing information.',
|
|
11
11
|
'bank-details-only': 'Provide your preferred bank account details in a secure and simple way, without oversharing information.',
|
|
12
|
+
'verify-age': 'Verify your age through an organisation you already have an account with, like your bank.',
|
|
12
13
|
};
|
|
13
14
|
export const IntroText = ({ type }) => {
|
|
14
15
|
const theme = useContext(ThemeContext);
|
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, compabilityWarnings: CompabilityWarnings) => {
|
|
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, compabilityWarnings: CompabilityWarnings) => 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' |
|
|
@@ -43,7 +43,7 @@ Your page should render a widget similar to this 👇 And when the button is cli
|
|
|
43
43
|
| rpDataSharingDescription | The text that goes under the popup title explaining how shared data will be used. | string | no | 'Your details will be shared with the current website.' |
|
|
44
44
|
| enableManualVerification | Whether an option to verify identity manually is displayed or not. | boolean | no | false |
|
|
45
45
|
| type | The type of ConnectID button widget. [More details here](#type) | 'tiny' \| 'medium benefits' \| 'medium process' \| 'full benefits' \| 'full process' | no | undefined (shows only ConnectID button). |
|
|
46
|
-
| introTextType | The text that goes above the ConnectID button. [More details here](#introTextType) | 'verify-trust' \| 'verify-account' \| 'power' \| 'sign-up' \| 'bank-details-plus-personal-info' \| 'bank-details-only' | yes when `type` is set. | - |
|
|
46
|
+
| introTextType | The text that goes above the ConnectID button. [More details here](#introTextType) | 'verify-trust' \| 'verify-account' \| 'power' \| 'sign-up' \| 'bank-details-plus-personal-info' \| 'bank-details-only' \| 'verify-age' | yes when `type` is set. | - |
|
|
47
47
|
| fullProcessCollapsible | Whether process items are collapsible or not. Only available when `type` is `full process`. | boolean | no | false |
|
|
48
48
|
| showAllParticipants | Show all participants regardless of any other option. **_This should only be used for testing_**. | boolean | no | false |
|
|
49
49
|
| theme | ConnectID button theme. | 'dark' \| 'light' | no | 'dark' |
|
|
@@ -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
|
+
#### CompabilityWarnings type
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
export type CompabilityWarnings = {
|
|
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
|
|
@@ -188,6 +206,7 @@ All elements have classes that can be styled with CSS to match you brand identit
|
|
|
188
206
|
`sign-up`: ConnectID lets you sign up using details stored with an organisation you already trust, like your bank.\
|
|
189
207
|
`bank-details-plus-personal-info`: Provide your preferred bank account and personal details in a secure and simple way, without oversharing information.\
|
|
190
208
|
`bank-details-only`: Provide your preferred bank account details in a secure and simple way, without oversharing information.
|
|
209
|
+
`verify-age`: Verify your age through an organisation you already have an account with, like your bank.
|
|
191
210
|
</details>
|
|
192
211
|
|
|
193
212
|
### <a name="infoContent"></a>infoContent option
|
|
@@ -222,6 +241,12 @@ The SVG image below the ConnectID button has either option dark or light mode. B
|
|
|
222
241
|
|
|
223
242
|
## 🚀 Release Notes
|
|
224
243
|
|
|
244
|
+
### 6.10.0-alpha.0 (Jun 10, 2025)
|
|
245
|
+
- Add `compabilityWarnings` to `onProceed` callback.
|
|
246
|
+
|
|
247
|
+
### 6.9.0 (Jun 5, 2025)
|
|
248
|
+
- Add new `introTextType` option: `verify-age`.
|
|
249
|
+
|
|
225
250
|
### 6.8.0 (May 7, 2025)
|
|
226
251
|
- Show message in the IDP list when any IDP with `ProfileType=Redirect` and `ProfileVariant=FAPI2 Adv. OP w/Private Key, PAR` is not being displayed.
|
|
227
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
|
});
|
package/compability.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { CompabilityEnvironment, CompabilityWarnings } from './types';
|
|
2
|
+
export declare const getCompability: (env: CompabilityEnvironment) => CompabilityWarnings;
|
|
3
|
+
export declare const getMightFail: (env: Pick<CompabilityEnvironment, "idpHasAppCertification" | "isMobile" | "isIncognito" | "isAndroid" | "isIos" | "isSamsungBrowser" | "isChromeBrowser">) => boolean;
|
package/compability.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getBrowserName } from './userAgent';
|
|
2
|
+
export const getCompability = (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
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { getCompability, getMightFail } from './compability';
|
|
4
|
+
describe('getCompability', () => {
|
|
5
|
+
it('should return compability warnings', () => {
|
|
6
|
+
const compability = getCompability({
|
|
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(compability, {
|
|
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
|
+
});
|
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.0",
|
|
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.
|
|
30
|
-
"@types/react": "^18.3.
|
|
29
|
+
"@types/node": "^22.15.31",
|
|
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;
|
|
@@ -28,7 +28,7 @@ export type AnalyticsEvent = 'ConnectID Button Displayed' | 'ConnectID Button Cl
|
|
|
28
28
|
export type Analytics = {
|
|
29
29
|
track: (event: AnalyticsEvent, data?: Record<string, unknown>) => void;
|
|
30
30
|
};
|
|
31
|
-
export type IntroTextType = 'verify-trust' | 'verify-account' | 'power' | 'sign-up' | 'bank-details-plus-personal-info' | 'bank-details-only';
|
|
31
|
+
export type IntroTextType = 'verify-trust' | 'verify-account' | 'power' | 'sign-up' | 'bank-details-plus-personal-info' | 'bank-details-only' | 'verify-age';
|
|
32
32
|
export type CidButtonType = 'tiny' | 'medium benefits' | 'medium process' | 'full benefits' | 'full process';
|
|
33
33
|
export type CidButtonTypeConfig = {
|
|
34
34
|
hasIntro: boolean;
|
|
@@ -38,4 +38,19 @@ export type CidButtonTypeConfig = {
|
|
|
38
38
|
hasHowItWorks: boolean;
|
|
39
39
|
hasLearnMore: boolean;
|
|
40
40
|
};
|
|
41
|
-
export {
|
|
41
|
+
export type CompabilityEnvironment = {
|
|
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 CompabilityWarnings = {
|
|
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
|
+
};
|