Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/add-linkedin-microsoft-idp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@forgerock/login-widget': minor
---

Add LinkedIn and Microsoft Entra ID as OOTB identity providers in the SelectIdP callback.

- LinkedIn: matches `buttonDisplayName` containing `'LinkedIn'`, renders branded button with `#0077b5` background and the LinkedIn "in" logo.
- Microsoft/Entra ID: matches `buttonDisplayName` containing `'Microsoft'`, renders branded button with `#0072c6` background and the 4-color Microsoft squares logo.
- Dark mode variants for both providers follow the existing pattern (white background, brand-color text).
- Unknown providers continue to render nothing — no change to existing fallback behavior.
26 changes: 26 additions & 0 deletions core/components/icons/linkedin-icon.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!--

Copyright © 2026 Ping Identity Corporation. All right reserved.

This software may be modified and distributed under the terms
of the MIT license. See the LICENSE file for details.

-->

<script lang="ts">
export let classes = '';
export let size = '24px';
</script>

<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
fill="currentColor"
class={classes}
viewBox="0 0 24 24"
>
<path
d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 0 1-2.063-2.065 2.064 2.064 0 1 1 2.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"
/>
</svg>
26 changes: 26 additions & 0 deletions core/components/icons/microsoft-icon.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!--

Copyright © 2026 Ping Identity Corporation. All right reserved.

This software may be modified and distributed under the terms
of the MIT license. See the LICENSE file for details.

-->

<script lang="ts">
export let classes = '';
export let size = '24px';
</script>

<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
viewBox="0 0 21 21"
class={classes}
>
<path fill="#f25022" d="M1 1h9v9H1z" />
<path fill="#7fba00" d="M11 1h9v9H11z" />
<path fill="#00a4ef" d="M1 11h9v9H1z" />
<path fill="#ffb900" d="M11 11h9v9H11z" />
</svg>
160 changes: 159 additions & 1 deletion core/journey/callbacks/select-idp/select-idp.mock.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
*
* Copyright © 2025 Ping Identity Corporation. All right reserved.
* Copyright © 2025-2026 Ping Identity Corporation. All right reserved.
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
Expand Down Expand Up @@ -60,6 +60,36 @@ export const multipleProvidersLocalAuthFormStep = {
iconBackground: '#4184f3',
},
},
{
provider: 'linkedin-web',
uiConfig: {
buttonCustomStyle:
'background-color: #0077b5; color: white; border-color: #0077b5;',
buttonImage: '',
buttonClass: '',
buttonCustomStyleHover:
'background-color: #006097; color: white; border-color: #006097;',
buttonDisplayName: 'LinkedIn',
iconClass: '',
iconFontColor: 'white',
iconBackground: '#0077b5',
},
},
{
provider: 'microsoft-web',
uiConfig: {
buttonCustomStyle:
'background-color: #0072c6; color: white; border-color: #0072c6;',
buttonImage: '',
buttonClass: '',
buttonCustomStyleHover:
'background-color: #005a9e; color: white; border-color: #005a9e;',
buttonDisplayName: 'Microsoft',
iconClass: '',
iconFontColor: 'white',
iconBackground: '#0072c6',
},
},
{ provider: 'localAuthentication' },
],
},
Expand Down Expand Up @@ -136,6 +166,36 @@ export const multipleProvidersLocalAuthNoFormStep = {
iconBackground: '#4184f3',
},
},
{
provider: 'linkedin-web',
uiConfig: {
buttonCustomStyle:
'background-color: #0077b5; color: white; border-color: #0077b5;',
buttonImage: '',
buttonClass: '',
buttonCustomStyleHover:
'background-color: #006097; color: white; border-color: #006097;',
buttonDisplayName: 'LinkedIn',
iconClass: '',
iconFontColor: 'white',
iconBackground: '#0077b5',
},
},
{
provider: 'microsoft-web',
uiConfig: {
buttonCustomStyle:
'background-color: #0072c6; color: white; border-color: #0072c6;',
buttonImage: '',
buttonClass: '',
buttonCustomStyleHover:
'background-color: #005a9e; color: white; border-color: #005a9e;',
buttonDisplayName: 'Microsoft',
iconClass: '',
iconFontColor: 'white',
iconBackground: '#0072c6',
},
},
{ provider: 'localAuthentication' },
],
},
Expand Down Expand Up @@ -200,6 +260,36 @@ export const multipleProvidersNoLocalAuthStep = {
iconBackground: '#4184f3',
},
},
{
provider: 'linkedin-web',
uiConfig: {
buttonCustomStyle:
'background-color: #0077b5; color: white; border-color: #0077b5;',
buttonImage: '',
buttonClass: '',
buttonCustomStyleHover:
'background-color: #006097; color: white; border-color: #006097;',
buttonDisplayName: 'LinkedIn',
iconClass: '',
iconFontColor: 'white',
iconBackground: '#0077b5',
},
},
{
provider: 'microsoft-web',
uiConfig: {
buttonCustomStyle:
'background-color: #0072c6; color: white; border-color: #0072c6;',
buttonImage: '',
buttonClass: '',
buttonCustomStyleHover:
'background-color: #005a9e; color: white; border-color: #005a9e;',
buttonDisplayName: 'Microsoft',
iconClass: '',
iconFontColor: 'white',
iconBackground: '#0072c6',
},
},
],
},
{ name: 'value', value: '' },
Expand Down Expand Up @@ -327,6 +417,74 @@ export const singleProviderNoLocalAuthStep = {
],
};

export const singleLinkedInProviderStep = {
authId:
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoSW5kZXhWYWx1ZSI6IlNvY2lhbExvZ2luLXdlYiIsIm90ayI6ImFpMzZ2NHZwZ243c2JnZGo1YTN2aHZiM3V0IiwiYXV0aEluZGV4VHlwZSI6InNlcnZpY2UiLCJyZWFsbSI6Ii9hbHBoYSIsInNlc3Npb25JZCI6IipBQUpUU1FBQ01ESUFCSFI1Y0dVQUNFcFhWRjlCVlZSSUFBSlRNUUFDTURFLipleUowZVhBaU9pSktWMVFpTENKamRIa2lPaUpLVjFRaUxDSmhiR2NpT2lKSVV6STFOaUo5LlpYbEtNR1ZZUVdsUGFVcExWakZSYVV4RFNteGliVTFwVDJsS1FrMVVTVFJSTUVwRVRGVm9WRTFxVlRKSmFYZHBXVmQ0YmtscWIybGFSMng1U1c0d0xpNW5hRmRCWlVweVRtOTNYekZFY1RJNU4wUkhka2gzTGxJemVsWlJiblJWVkZFMVozWmFjVVZJTkdVMWJUTlFiRUZOVjNwU1RWZzNTV0ZVYld4c1RqRlpjakZQY0U5T1EySlViVEEyTFRsVlQwbDBNbEpQY0RsV2NXWlNUM2xtYWtSWldGWnhTV0l4UVZGdlpreFFRamhPVG10ak1Xb3pMVXBhUVVkNFlrRlRXRTR5U1daaVdFSlVOVlp3Y2xCMWVHMDNOMGcwV2tKSlNYcHRVek5mWVU1clJtRkNSbkJEYzNOUWNteERWM1F3YWxVNVMxTlFRVUpFUkhkRVRFbFRiMnBET1hCQ1NGbzVTRUZxTWxsUlRVUmFialk1VWpORE0xYzBNekZCYTB4d1ZWTmpSSFZEUkdacFEzVm5hVTlDYzJaemR6Tk1OWGt3TVhoZk5rUkVTMnBwT0ZWSGRIVkZWa3B4VGxGcVVUaExWMEZGZFZWSFNVbEthVkZIWjFOa2F6bHRXR1F3ZFhJeU5GcFFNV2hZVkROWVIxWnlaMmc0U2pGU2RUVlZaWFk0WjBobk5FTTNjR2R2ZEVoUFIyVnBWMk5QUTBoSlpXSndTalJWWm5odU1XVnVkVEpaVjFaSFVWTTFiMFo1WVRGalZWSlBPVTA0TUdadVZFTTJZVGh2YkV4c09XVkhZbFE1TkUxVFJYZG5NVk5QTFc1MGJsaHliR1J6TWxvMmVXMXlhR2hxTlc4MGRucEhTV1JvWDIxMU5rVkROMUpMWTFReFlXZGtkRXRUWDBZdFFXaHBZMVpCUWtGc1JrdDZTWGxSTFRRdExUSmpRMDFXWVRoRFRVbHlVV0U1YXpjMlRrWk9Sak5IY2twaGJIZFpiVzlrUnpGc0xWRnRRVmQyZVZrelRVSnJRa1l4UmxKMmFuaDZMVkZHZUd4d1J6SlJOR1p6VkZSVkxYVjFhakl5VWpneWJqQkhhVnBVZDFkbVZVbE9WV2x0VUdWSWVrSnBZbEU1TjJ4dlYwaHZkMUoxVEV3NE5rUjZOVTFVZDB0eFNUQTJNak42V0UweVJuVkJTRUp0V1ROc1NHdHhkWEptWnpOTmRrUjRZVE5mVTNFMmRXZEdPWG8wU2xCVVp6UTRWWE5rUWtwdVduZEdlRlF4TXpoTFdGbFphMUV5Y0dvdU4wWkhRa2QwZG5STE1FRlVibWxHY1ZacVIwTlJady5xa1NtUTM1X253X0lCUU11RlRueW5NMTN1NmFoTGR2bkVYRVhSaTNFSFBjIiwiZXhwIjoxNjY5OTc3OTg1LCJpYXQiOjE2Njk5Nzc2ODV9.iV-WX2dk_8oTug-zlou7SvL29CwfaCSlzR5N0huRsxc',
callbacks: [
{
type: 'SelectIdPCallback',
output: [
{
name: 'providers',
value: [
{
provider: 'linkedin-web',
uiConfig: {
buttonCustomStyle:
'background-color: #0077b5; color: white; border-color: #0077b5;',
buttonImage: '',
buttonClass: '',
buttonCustomStyleHover:
'background-color: #006097; color: white; border-color: #006097;',
buttonDisplayName: 'LinkedIn',
iconClass: '',
iconFontColor: 'white',
iconBackground: '#0077b5',
},
},
],
},
{ name: 'value', value: '' },
],
input: [{ name: 'IDToken1', value: '' }],
},
],
};

export const singleMicrosoftProviderStep = {
authId:
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoSW5kZXhWYWx1ZSI6IlNvY2lhbExvZ2luLXdlYiIsIm90ayI6ImFpMzZ2NHZwZ243c2JnZGo1YTN2aHZiM3V0IiwiYXV0aEluZGV4VHlwZSI6InNlcnZpY2UiLCJyZWFsbSI6Ii9hbHBoYSIsInNlc3Npb25JZCI6IipBQUpUU1FBQ01ESUFCSFI1Y0dVQUNFcFhWRjlCVlZSSUFBSlRNUUFDTURFLipleUowZVhBaU9pSktWMVFpTENKamRIa2lPaUpLVjFRaUxDSmhiR2NpT2lKSVV6STFOaUo5LlpYbEtNR1ZZUVdsUGFVcExWakZSYVV4RFNteGliVTFwVDJsS1FrMVVTVFJSTUVwRVRGVm9WRTFxVlRKSmFYZHBXVmQ0YmtscWIybGFSMng1U1c0d0xpNW5hRmRCWlVweVRtOTNYekZFY1RJNU4wUkhka2gzTGxJemVsWlJiblJWVkZFMVozWmFjVVZJTkdVMWJUTlFiRUZOVjNwU1RWZzNTV0ZVYld4c1RqRlpjakZQY0U5T1EySlViVEEyTFRsVlQwbDBNbEpQY0RsV2NXWlNUM2xtYWtSWldGWnhTV0l4UVZGdlpreFFRamhPVG10ak1Xb3pMVXBhUVVkNFlrRlRXRTR5U1daaVdFSlVOVlp3Y2xCMWVHMDNOMGcwV2tKSlNYcHRVek5mWVU1clJtRkNSbkJEYzNOUWNteERWM1F3YWxVNVMxTlFRVUpFUkhkRVRFbFRiMnBET1hCQ1NGbzVTRUZxTWxsUlRVUmFialk1VWpORE0xYzBNekZCYTB4d1ZWTmpSSFZEUkdacFEzVm5hVTlDYzJaemR6Tk1OWGt3TVhoZk5rUkVTMnBwT0ZWSGRIVkZWa3B4VGxGcVVUaExWMEZGZFZWSFNVbEthVkZIWjFOa2F6bHRXR1F3ZFhJeU5GcFFNV2hZVkROWVIxWnlaMmc0U2pGU2RUVlZaWFk0WjBobk5FTTNjR2R2ZEVoUFIyVnBWMk5QUTBoSlpXSndTalJWWm5odU1XVnVkVEpaVjFaSFVWTTFiMFo1WVRGalZWSlBPVTA0TUdadVZFTTJZVGh2YkV4c09XVkhZbFE1TkUxVFJYZG5NVk5QTFc1MGJsaHliR1J6TWxvMmVXMXlhR2hxTlc4MGRucEhTV1JvWDIxMU5rVkROMUpMWTFReFlXZGtkRXRUWDBZdFFXaHBZMVpCUWtGc1JrdDZTWGxSTFRRdExUSmpRMDFXWVRoRFRVbHlVV0U1YXpjMlRrWk9Sak5IY2twaGJIZFpiVzlrUnpGc0xWRnRRVmQyZVZrelRVSnJRa1l4UmxKMmFuaDZMVkZHZUd4d1J6SlJOR1p6VkZSVkxYVjFhakl5VWpneWJqQkhhVnBVZDFkbVZVbE9WV2x0VUdWSWVrSnBZbEU1TjJ4dlYwaHZkMUoxVEV3NE5rUjZOVTFVZDB0eFNUQTJNak42V0UweVJuVkJTRUp0V1ROc1NHdHhkWEptWnpOTmRrUjRZVE5mVTNFMmRXZEdPWG8wU2xCVVp6UTRWWE5rUWtwdVduZEdlRlF4TXpoTFdGbFphMUV5Y0dvdU4wWkhRa2QwZG5STE1FRlVibWxHY1ZacVIwTlJady5xa1NtUTM1X253X0lCUU11RlRueW5NMTN1NmFoTGR2bkVYRVhSaTNFSFBjIiwiZXhwIjoxNjY5OTc3OTg1LCJpYXQiOjE2Njk5Nzc2ODV9.iV-WX2dk_8oTug-zlou7SvL29CwfaCSlzR5N0huRsxc',
callbacks: [
{
type: 'SelectIdPCallback',
output: [
{
name: 'providers',
value: [
{
provider: 'microsoft-web',
uiConfig: {
buttonCustomStyle:
'background-color: #0072c6; color: white; border-color: #0072c6;',
buttonImage: '',
buttonClass: '',
buttonCustomStyleHover:
'background-color: #005a9e; color: white; border-color: #005a9e;',
buttonDisplayName: 'Microsoft',
iconClass: '',
iconFontColor: 'white',
iconBackground: '#0072c6',
},
},
],
},
{ name: 'value', value: '' },
],
input: [{ name: 'IDToken1', value: '' }],
},
],
};

export const docsExample = {
type: 'SelectIdPCallback',
output: [
Expand Down
22 changes: 21 additions & 1 deletion core/journey/callbacks/select-idp/select-idp.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@
import { callbackType } from '@forgerock/journey-client';

import { createJourneyStep } from '$journey/_utilities/step.mock';
import { singleProviderNoLocalAuthStep } from './select-idp.mock';
import {
singleLinkedInProviderStep,
singleMicrosoftProviderStep,
singleProviderNoLocalAuthStep,
} from './select-idp.mock';
import Input from './select-idp.story.svelte';

const singleProviderNoLocalAuth = createJourneyStep(singleProviderNoLocalAuthStep);
const singleLinkedInProvider = createJourneyStep(singleLinkedInProviderStep);
const singleMicrosoftProvider = createJourneyStep(singleMicrosoftProviderStep);

export default {
argTypes: {
Expand All @@ -32,3 +38,17 @@ export const Base = {
localAuth: true,
},
};

export const LinkedIn = {
args: {
socialCallback: singleLinkedInProvider.getCallbackOfType(callbackType.SelectIdPCallback),
localAuth: false,
},
};

export const Microsoft = {
args: {
socialCallback: singleMicrosoftProvider.getCallbackOfType(callbackType.SelectIdPCallback),
localAuth: false,
},
};
24 changes: 24 additions & 0 deletions core/journey/callbacks/select-idp/select-idp.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import AppleIcon from '../../../components/icons/apple-icon.svelte';
import FacebookIcon from '../../../components/icons/facebook-icon.svelte';
import GoogleIcon from '../../../components/icons/google-icon.svelte';
import LinkedinIcon from '../../../components/icons/linkedin-icon.svelte';
import MicrosoftIcon from '../../../components/icons/microsoft-icon.svelte';

import type { SelectIdPCallback } from '@forgerock/journey-client/types';
import type { z } from 'zod';
Expand Down Expand Up @@ -107,6 +109,28 @@
<T key="continueWith" />
{idp.text}
</Button>
{:else if idp.text.toUpperCase().includes('LINKEDIN')}
<Button
classes="tw_button-linkedin dark:tw_button-linkedin_dark"
type="button"
width="auto"
onClick={() => setBtnValue(idp.value)}
>
<LinkedinIcon classes="tw_inline-block tw_fill-current" />
<T key="continueWith" />
{idp.text}
</Button>
{:else if idp.text.toUpperCase().includes('MICROSOFT')}
<Button
classes="tw_button-microsoft dark:tw_button-microsoft_dark"
type="button"
width="auto"
onClick={() => setBtnValue(idp.value)}
>
<MicrosoftIcon classes="tw_inline-block tw_fill-current" />
<T key="continueWith" />
{idp.text}
</Button>
{/if}
</Grid>
{/each}
Expand Down
38 changes: 38 additions & 0 deletions themes/default/journey.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,44 @@ module.exports = function (theme) {
opacity: `0.1`,
},
},
'.button-linkedin': {
'--border-color':
'hsl(var(--tw-colors-secondary-light-hs),var(--tw-colors-secondary-light-l))',
borderColor: `var(--border-color, ${theme('colors.secondary.light')})`,
backgroundColor: '#0077b5',
color: theme('colors.white'),
'&:hover::before, &:focus::before': {
opacity: `0.1`,
},
},
'.button-linkedin_dark': {
'--border-color':
'hsl(var(--tw-colors-secondary-light-hs),var(--tw-colors-secondary-light-l))',
borderColor: `var(--border-color, ${theme('colors.secondary.light')})`,
backgroundColor: theme('colors.white'),
color: '#0077b5',
'&:hover::before, &:focus::before': {
opacity: `0.1`,
},
},
'.button-microsoft': {
'--border-color': '#8C8C8C',
borderColor: `var(--border-color, #8C8C8C)`,
backgroundColor: theme('colors.white'),
color: theme('colors.black'),
'&:hover::before, &:focus::before': {
opacity: `0.1`,
},
},
'.button-microsoft_dark': {
'--border-color': '#8C8C8C',
borderColor: `var(--border-color, #8C8C8C)`,
backgroundColor: theme('colors.white'),
color: theme('colors.black'),
'&:hover::before, &:focus::before': {
opacity: `0.1`,
},
},
'.input-policies': {
'--font-size': 'var(--tw-font-size-sm-type)',
fontSize: `var(--font-size, ${theme('fontSize.sm')})`,
Expand Down
Loading