diff --git a/.env.example b/.env.example index 4a7910e..708c880 100644 --- a/.env.example +++ b/.env.example @@ -57,7 +57,7 @@ RSD_ENVIRONMENT=prod # consumed by services: authentication, frontend (api/fe) # provide a list of supported OpenID auth providers # the values should be separated by semicolon (;) -# Allowed values are: SURFCONEXT, HELMHOLTZID, ORCID or LOCAL +# Allowed values are: SURFCONEXT, ORCID, AZURE, LINKEDIN or LOCAL # if env value is not provided default provider is set to be SURFCONEXT # if you add the value "LOCAL", then local accounts are enabled, USE THIS FOR TESTING PURPOSES ONLY RSD_AUTH_PROVIDERS=SURFCONEXT;ORCID;AZURE;LOCAL @@ -121,6 +121,14 @@ AZURE_DESCRIPTION_HTML="Sign in with your institutional credentials" # the organisation recorded for users logged in via this provider AZURE_ORGANISATION= +# LINKEDIN +# consumed by: authentication, frontend/pages/api/fe/auth/ +LINKEDIN_CLIENT_ID= +# consumed by: authentication, frontend/pages/api/fe/auth/ +LINKEDIN_REDIRECT=http://localhost/auth/login/linkedin +# consumed by: authentication, frontend/pages/api/fe/auth/ +LINKEDIN_WELL_KNOWN_URL=https://www.linkedin.com/oauth/.well-known/openid-configuration + # max requests to the GitHub API per run, runs 10 times per hour # optional, comment out if not available, a default of 6 will be used # consumed by: scrapers @@ -170,6 +178,10 @@ AUTH_ORCID_CLIENT_SECRET= # consumed by services: authentication AUTH_AZURE_CLIENT_SECRET= +# LinkedIn +# consumed by services: authentication +AUTH_LINKEDIN_CLIENT_SECRET= + # consumed by: scrapers # optional, comment out if not available, should be of the form username:token # obtain the secret from GITHUB dashboard diff --git a/deployment/.env.example b/deployment/.env.example index 18d5f22..eea82c2 100644 --- a/deployment/.env.example +++ b/deployment/.env.example @@ -57,7 +57,7 @@ RSD_ENVIRONMENT=prod # consumed by services: authentication, frontend (api/fe) # provide a list of supported OpenID auth providers # the values should be separated by semicolon (;) -# Allowed values are: SURFCONEXT, HELMHOLTZID, ORCID or LOCAL +# Allowed values are: SURFCONEXT, ORCID, AZURE, LINKEDIN or LOCAL # if env value is not provided default provider is set to be SURFCONEXT # if you add the value "LOCAL", then local accounts are enabled, USE THIS FOR TESTING PURPOSES ONLY RSD_AUTH_PROVIDERS=SURFCONEXT;LOCAL @@ -121,6 +121,14 @@ AZURE_DESCRIPTION_HTML="Sign in with your institutional credentials" # the organisation recorded for users logged in via this provider AZURE_ORGANISATION= +# LINKEDIN +# consumed by: authentication, frontend/pages/api/fe/auth/ +LINKEDIN_CLIENT_ID= +# consumed by: authentication, frontend/pages/api/fe/auth/ +LINKEDIN_REDIRECT=http://localhost/auth/login/linkedin +# consumed by: authentication, frontend/pages/api/fe/auth/ +LINKEDIN_WELL_KNOWN_URL=https://www.linkedin.com/oauth/.well-known/openid-configuration + # max requests to the GitHub API per run, runs 10 times per hour # optional, comment out if not available, a default of 6 will be used # consumed by: scrapers @@ -170,6 +178,10 @@ AUTH_ORCID_CLIENT_SECRET= # consumed by services: authentication AUTH_AZURE_CLIENT_SECRET= +# LinkedIn +# consumed by services: authentication +AUTH_LINKEDIN_CLIENT_SECRET= + # consumed by: scrapers # optional, comment out if not available, should be of the form username:token # obtain the secret from GITHUB dashboard diff --git a/deployment/docker-compose.yml b/deployment/docker-compose.yml index 259cd1e..eacce18 100644 --- a/deployment/docker-compose.yml +++ b/deployment/docker-compose.yml @@ -43,7 +43,7 @@ services: auth: container_name: auth - image: ghcr.io/research-software-directory/rsd-saas/auth:v2.17.0 + image: ghcr.io/research-software-directory/rsd-saas/auth:v2.28.0 expose: - 7000 environment: @@ -56,28 +56,22 @@ services: - SURFCONEXT_CLIENT_ID - SURFCONEXT_REDIRECT - SURFCONEXT_WELL_KNOWN_URL - - SURFCONEXT_SCOPES - - HELMHOLTZID_CLIENT_ID - - HELMHOLTZID_REDIRECT - - HELMHOLTZID_WELL_KNOWN_URL - - HELMHOLTZID_SCOPES - - HELMHOLTZID_USE_ALLOW_LIST - - HELMHOLTZID_ALLOW_LIST - ORCID_CLIENT_ID - ORCID_REDIRECT - ORCID_REDIRECT_COUPLE - ORCID_WELL_KNOWN_URL - - ORCID_SCOPES - AZURE_CLIENT_ID - AZURE_REDIRECT - AZURE_WELL_KNOWN_URL - - AZURE_SCOPES - AZURE_ORGANISATION - - HELMHOLTZID_ALLOW_EXTERNAL_USERS + - LINKEDIN_CLIENT_ID + - LINKEDIN_REDIRECT + - LINKEDIN_WELL_KNOWN_URL - AUTH_SURFCONEXT_CLIENT_SECRET - AUTH_HELMHOLTZID_CLIENT_SECRET - AUTH_ORCID_CLIENT_SECRET - AUTH_AZURE_CLIENT_SECRET + - AUTH_LINKEDIN_CLIENT_SECRET - PGRST_JWT_SECRET depends_on: - database @@ -125,11 +119,6 @@ services: - SURFCONEXT_WELL_KNOWN_URL - SURFCONEXT_SCOPES - SURFCONEXT_RESPONSE_MODE - - HELMHOLTZID_CLIENT_ID - - HELMHOLTZID_REDIRECT - - HELMHOLTZID_WELL_KNOWN_URL - - HELMHOLTZID_SCOPES - - HELMHOLTZID_RESPONSE_MODE - ORCID_CLIENT_ID - ORCID_REDIRECT - ORCID_REDIRECT_COUPLE @@ -142,6 +131,10 @@ services: - AZURE_LOGIN_PROMPT - AZURE_DISPLAY_NAME - AZURE_DESCRIPTION_HTML + - LINKEDIN_CLIENT_ID + - LINKEDIN_REDIRECT + - LINKEDIN_WELL_KNOWN_URL + - CROSSREF_CONTACT_EMAIL expose: - 3000 depends_on: diff --git a/docker-compose.yml b/docker-compose.yml index e911bbd..f4da0b9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,15 +1,4 @@ -# SPDX-FileCopyrightText: 2021 - 2023 Dusan Mijatovic (dv4all) -# SPDX-FileCopyrightText: 2021 - 2023 dv4all -# SPDX-FileCopyrightText: 2021 - 2024 Ewan Cahen (Netherlands eScience Center) -# SPDX-FileCopyrightText: 2021 - 2024 Netherlands eScience Center -# SPDX-FileCopyrightText: 2022 - 2024 Christian Meeßen (GFZ) -# SPDX-FileCopyrightText: 2022 - 2024 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences -# SPDX-FileCopyrightText: 2022 Helmholtz Centre for Environmental Research (UFZ) -# SPDX-FileCopyrightText: 2022 Matthias Rüster (GFZ) -# SPDX-FileCopyrightText: 2023 - 2024 Dusan Mijatovic (Netherlands eScience Center) -# SPDX-FileCopyrightText: 2023 Dusan Mijatovic (dv4all) (dv4all) -# -# SPDX-License-Identifier: Apache-2.0 +# THIS DOCKER FILE REQUIRES .env FILE # version: "3.0" @@ -50,7 +39,7 @@ services: - net auth: - image: ghcr.io/research-software-directory/rsd-saas/auth:v2.17.0 + image: ghcr.io/research-software-directory/rsd-saas/auth:v2.28.0 ports: - 5005:5005 expose: @@ -65,28 +54,22 @@ services: - SURFCONEXT_CLIENT_ID - SURFCONEXT_REDIRECT - SURFCONEXT_WELL_KNOWN_URL - - SURFCONEXT_SCOPES - - HELMHOLTZID_CLIENT_ID - - HELMHOLTZID_REDIRECT - - HELMHOLTZID_WELL_KNOWN_URL - - HELMHOLTZID_SCOPES - - HELMHOLTZID_USE_ALLOW_LIST - - HELMHOLTZID_ALLOW_LIST - ORCID_CLIENT_ID - ORCID_REDIRECT - ORCID_REDIRECT_COUPLE - ORCID_WELL_KNOWN_URL - - ORCID_SCOPES - AZURE_CLIENT_ID - AZURE_REDIRECT - AZURE_WELL_KNOWN_URL - - AZURE_SCOPES - AZURE_ORGANISATION - - HELMHOLTZID_ALLOW_EXTERNAL_USERS + - LINKEDIN_CLIENT_ID + - LINKEDIN_REDIRECT + - LINKEDIN_WELL_KNOWN_URL - AUTH_SURFCONEXT_CLIENT_SECRET - AUTH_HELMHOLTZID_CLIENT_SECRET - AUTH_ORCID_CLIENT_SECRET - AUTH_AZURE_CLIENT_SECRET + - AUTH_LINKEDIN_CLIENT_SECRET - PGRST_JWT_SECRET depends_on: - database @@ -127,7 +110,7 @@ services: # dockerfile to use for build dockerfile: Dockerfile # update version number to correspond to frontend/package.json - image: kin-rpd/frontend:0.0.1 + image: kin-rpd/frontend:0.0.2 environment: # it uses values from .env file - POSTGREST_URL @@ -142,11 +125,6 @@ services: - SURFCONEXT_WELL_KNOWN_URL - SURFCONEXT_SCOPES - SURFCONEXT_RESPONSE_MODE - - HELMHOLTZID_CLIENT_ID - - HELMHOLTZID_REDIRECT - - HELMHOLTZID_WELL_KNOWN_URL - - HELMHOLTZID_SCOPES - - HELMHOLTZID_RESPONSE_MODE - ORCID_CLIENT_ID - ORCID_REDIRECT - ORCID_REDIRECT_COUPLE @@ -159,12 +137,18 @@ services: - AZURE_LOGIN_PROMPT - AZURE_DISPLAY_NAME - AZURE_DESCRIPTION_HTML + - LINKEDIN_CLIENT_ID + - LINKEDIN_REDIRECT + - LINKEDIN_WELL_KNOWN_URL + - CROSSREF_CONTACT_EMAIL expose: - 3000 depends_on: - database - backend - auth + volumes: + - ./frontend/public:/app/public # volumes: # - ./deployment/hmz/styles:/app/public/styles # - ./deployment/hmz/data:/app/public/data diff --git a/frontend/auth/api/authEndpoint.ts b/frontend/auth/api/authEndpoint.ts new file mode 100644 index 0000000..7c3ca52 --- /dev/null +++ b/frontend/auth/api/authEndpoint.ts @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: 2024 - 2025 Dusan Mijatovic (Netherlands eScience Center) +// SPDX-FileCopyrightText: 2024 - 2025 Netherlands eScience Center +// SPDX-FileCopyrightText: 2025 Ewan Cahen (Netherlands eScience Center) +// +// SPDX-License-Identifier: Apache-2.0 + +import logger from '~/utils/logger' +import {getAuthorisationEndpoint} from './authHelpers' + +type providers = 'surfconext'|'helmholtzid'|'orcid'|'azure'|'linkedin' +// how often we refresh auth endpoint +const refreshInterval = 60*60*1000 +// save timer as public variable +let timer:NodeJS.Timeout +// save authorisation endpoint info +const cache:{ + [key:string]:{ + authEndpoint?: string + wellknownUrl: string + } +}={} + +/** + * We save authorization_endpoint in memory to avoid repeating calls + * refreshInterval defined how often we refresh auth endpoint info. + * + */ +export async function getAuthEndpoint(wellknownUrl:string,provider:providers){ + try{ + // if already present return existing value + if (cache?.[provider]?.authEndpoint) { + // console.log('getAuthEndpoint...CACHE used...', new Date()) + return cache[provider].authEndpoint + } + // if not present request endpoint info + cache[provider] = { + wellknownUrl, + authEndpoint: await getAuthorisationEndpoint(wellknownUrl) + } + // we set timer only in the production because hot-reloading creates multiple instances + if (process.env.NODE_ENV==='production'){ + // clear previous timer to avoid mem leaks + if (timer){ + // console.log('getAuthEndpoint...CLEAR INTERVAL...', new Date()) + clearInterval(timer) + } + // create refresh interval and store it + timer = setInterval(async()=>{ + // console.log('getAuthEndpoint...REFRESH INFO...', new Date()) + // refresh all cached providers + const providers = Object.keys(cache) + const requests = providers.map(provider=>{ + return getAuthorisationEndpoint(cache[provider].wellknownUrl) + }) + // perform all requests in parallel + const endpoints = await Promise.all(requests) + // update all providers + providers.forEach((provider,pos)=>{ + // update only if there is info + if (endpoints[pos]) { + // console.log(`getAuthEndpoint...${provider}...`, endpoints[pos]) + cache[provider].authEndpoint = endpoints[pos] + } + }) + },refreshInterval) + } + // console.log('getAuthEndpoint...REQUEST made...', new Date()) + return cache[provider]?.authEndpoint + }catch(e:any){ + logger(`getAuthEndpoint: ${e.message}`, 'error') + return undefined + } +} diff --git a/frontend/package.json b/frontend/package.json index 0a4e7fa..337b108 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "kin-rpd-frontend", - "version": "0.0.1", + "version": "0.0.2", "private": true, "scripts": { "dev": "next dev", diff --git a/frontend/pages/api/fe/auth/azure.ts b/frontend/pages/api/fe/auth/azure.ts index d141d45..11773d9 100644 --- a/frontend/pages/api/fe/auth/azure.ts +++ b/frontend/pages/api/fe/auth/azure.ts @@ -1,10 +1,10 @@ -// SPDX-FileCopyrightText: 2022 - 2023 Netherlands eScience Center +// SPDX-FileCopyrightText: 2022 - 2025 Ewan Cahen (Netherlands eScience Center) +// SPDX-FileCopyrightText: 2022 - 2025 Netherlands eScience Center // SPDX-FileCopyrightText: 2022 Dusan Mijatovic (dv4all) -// SPDX-FileCopyrightText: 2022 Ewan Cahen (Netherlands eScience Center) // SPDX-FileCopyrightText: 2022 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences // SPDX-FileCopyrightText: 2022 Matthias Rüster (GFZ) // SPDX-FileCopyrightText: 2022 dv4all -// SPDX-FileCopyrightText: 2023 Dusan Mijatovic (Netherlands eScience Center) +// SPDX-FileCopyrightText: 2023 - 2024 Dusan Mijatovic (Netherlands eScience Center) // // SPDX-License-Identifier: Apache-2.0 @@ -15,18 +15,19 @@ // Next.js API route support: https://nextjs.org/docs/api-routes/introduction import type {NextApiRequest, NextApiResponse} from 'next' -import {getAuthorisationEndpoint, RedirectToProps, getRedirectUrl} from '~/auth/api/authHelpers' import logger from '~/utils/logger' +import {RedirectToProps, getRedirectUrl} from '~/auth/api/authHelpers' +import {getAuthEndpoint} from '~/auth/api/authEndpoint' import {Provider, ApiError} from '.' type Data = Provider | ApiError export async function azureRedirectProps() { - // extract wellknow url from env + // extract wellknown url from env const wellknownUrl = process.env.AZURE_WELL_KNOWN_URL ?? null if (wellknownUrl) { - // extract authorisation endpoint from wellknow response - const authorization_endpoint = await getAuthorisationEndpoint(wellknownUrl) + // get (cached) authorisation endpoint from wellknown url + const authorization_endpoint = await getAuthEndpoint(wellknownUrl, 'azure') if (authorization_endpoint) { // construct all props needed for redirectUrl const props: RedirectToProps = { @@ -51,7 +52,7 @@ export async function azureRedirectProps() { } export async function azureInfo() { - // extract all props from env and wellknow endpoint + // extract all props from env and wellknown endpoint const redirectProps = await azureRedirectProps() if (redirectProps) { // create return url and the name to use in login button @@ -71,7 +72,7 @@ export default async function handler( res: NextApiResponse ) { try { - // extract all props from env and wellknow endpoint + // extract all props from env and wellknown endpoint // and create return url and the name to use in login button const loginInfo = await azureInfo() if (loginInfo) { diff --git a/frontend/pages/api/fe/auth/helmholtzid.ts b/frontend/pages/api/fe/auth/helmholtzid.ts index 2daefee..b9caff3 100644 --- a/frontend/pages/api/fe/auth/helmholtzid.ts +++ b/frontend/pages/api/fe/auth/helmholtzid.ts @@ -3,8 +3,9 @@ // SPDX-FileCopyrightText: 2022 Dusan Mijatovic (dv4all) // SPDX-FileCopyrightText: 2022 Matthias Rüster (GFZ) // SPDX-FileCopyrightText: 2022 dv4all -// SPDX-FileCopyrightText: 2023 Dusan Mijatovic (Netherlands eScience Center) -// SPDX-FileCopyrightText: 2023 Netherlands eScience Center +// SPDX-FileCopyrightText: 2023 - 2024 Dusan Mijatovic (Netherlands eScience Center) +// SPDX-FileCopyrightText: 2023 - 2025 Netherlands eScience Center +// SPDX-FileCopyrightText: 2025 Ewan Cahen (Netherlands eScience Center) // // SPDX-License-Identifier: Apache-2.0 @@ -15,14 +16,15 @@ // Next.js API route support: https://nextjs.org/docs/api-routes/introduction import type {NextApiRequest, NextApiResponse} from 'next' -import {getAuthorisationEndpoint, RedirectToProps, getRedirectUrl} from '~/auth/api/authHelpers' import logger from '~/utils/logger' +import {RedirectToProps, getRedirectUrl} from '~/auth/api/authHelpers' +import {getAuthEndpoint} from '~/auth/api/authEndpoint' import {Provider, ApiError} from '.' type Data = Provider | ApiError const claims = { - id_token:{ + id_token: { schac_home_organization: null, name: null, email: null @@ -33,8 +35,8 @@ async function helmholtzRedirectProps() { // extract wellknow url from env const wellknownUrl = process.env.HELMHOLTZID_WELL_KNOWN_URL ?? null if (wellknownUrl) { - // extract authorisation endpoint from wellknow response - const authorization_endpoint = await getAuthorisationEndpoint(wellknownUrl) + // get (cached) authorisation endpoint from wellknown url + const authorization_endpoint = await getAuthEndpoint(wellknownUrl, 'helmholtzid') if (authorization_endpoint) { // construct all props needed for redirectUrl // use default values if env not provided @@ -60,7 +62,7 @@ async function helmholtzRedirectProps() { } export async function helmholtzInfo() { - // extract all props from env and wellknow endpoint + // extract all props from env and wellknown endpoint const redirectProps = await helmholtzRedirectProps() if (redirectProps) { // create return url and the name to use in login button @@ -82,7 +84,7 @@ export default async function handler( res: NextApiResponse ) { try { - // extract all props from env and wellknow endpoint + // extract all props from env and wellknown endpoint // and create return url and the name to use in login button const loginInfo = await helmholtzInfo() if (loginInfo) { diff --git a/frontend/pages/api/fe/auth/index.ts b/frontend/pages/api/fe/auth/index.ts index 052e76c..4a736a4 100644 --- a/frontend/pages/api/fe/auth/index.ts +++ b/frontend/pages/api/fe/auth/index.ts @@ -1,8 +1,8 @@ -// SPDX-FileCopyrightText: 2022 - 2023 Netherlands eScience Center +// SPDX-FileCopyrightText: 2022 - 2025 Ewan Cahen (Netherlands eScience Center) +// SPDX-FileCopyrightText: 2022 - 2025 Netherlands eScience Center // SPDX-FileCopyrightText: 2022 Dusan Mijatovic (dv4all) -// SPDX-FileCopyrightText: 2022 Ewan Cahen (Netherlands eScience Center) // SPDX-FileCopyrightText: 2022 dv4all -// SPDX-FileCopyrightText: 2023 Dusan Mijatovic (Netherlands eScience Center) +// SPDX-FileCopyrightText: 2023 - 2024 Dusan Mijatovic (Netherlands eScience Center) // SPDX-FileCopyrightText: 2024 Christian Meeßen (GFZ) // SPDX-FileCopyrightText: 2024 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences // @@ -23,6 +23,7 @@ import {helmholtzInfo} from './helmholtzid' import {localInfo} from './local' import {orcidInfo} from './orcid' import {azureInfo} from './azure' +import {linkedinInfo} from './linkedin' import logger from '~/utils/logger' export type ApiError = { @@ -38,9 +39,6 @@ export type Provider = { type Data = Provider[] | ApiError -// cached list of providers -let loginProviders:Provider[] = [] - async function getRedirectInfo(provider: string) { // select provider switch (provider.toLocaleLowerCase()) { @@ -55,6 +53,8 @@ async function getRedirectInfo(provider: string) { return orcidInfo() case 'azure': return azureInfo() + case 'linkedin': + return linkedinInfo() default: const message = `${provider} NOT SUPPORTED, check your spelling` logger(`api/fe/auth/providers: ${message}`, 'error') @@ -62,38 +62,29 @@ async function getRedirectInfo(provider: string) { } } -async function getProvidersInfo(){ - // only if we did not loaded info previously - if (loginProviders.length === 0){ - // extract list of providers, default value surfconext - const strProviders = process.env.RSD_AUTH_PROVIDERS || 'surfconext' - // split providers to array on ; - const providers = strProviders.split(';') +async function getProvidersInfo() { + // extract list of providers, default value surfconext + const strProviders = process.env.RSD_AUTH_PROVIDERS || 'surfconext' + // split providers to array on ; + const providers = strProviders.split(';') - // add all requests - const promises: Promise[] = [] - providers.forEach(provider => { - promises.push( - getRedirectInfo(provider) - ) - }) - // return providers with redirectUrl - const resp = await Promise.allSettled(promises) - // filter null responses (if any) - const info: Provider[] = [] - resp.forEach(item => { - if (item.status === 'fulfilled') { - info.push(item.value as Provider) - } - }) - // save response into cached variable - loginProviders = [ - ...info - ] - return loginProviders - } - // console.log("getProvidersInfo...cached...loginProviders") - return loginProviders + // add all requests + const promises: Promise[] = [] + providers.forEach(provider => { + promises.push( + getRedirectInfo(provider) + ) + }) + // return providers with redirectUrl + const resp = await Promise.allSettled(promises) + // filter null responses (if any) + const info: Provider[] = [] + resp.forEach(item => { + if (item.status === 'fulfilled' && item.value !== null) { + info.push(item.value) + } + }) + return info } export default async function handler( diff --git a/frontend/pages/api/fe/auth/linkedin.ts b/frontend/pages/api/fe/auth/linkedin.ts new file mode 100644 index 0000000..22ee5ff --- /dev/null +++ b/frontend/pages/api/fe/auth/linkedin.ts @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: 2025 Ewan Cahen (Netherlands eScience Center) +// SPDX-FileCopyrightText: 2025 Netherlands eScience Center +// +// SPDX-License-Identifier: Apache-2.0 + +import {getAuthEndpoint} from '~/auth/api/authEndpoint' +import {getRedirectUrl, RedirectToProps} from '~/auth/api/authHelpers' +import logger from '~/utils/logger' + +export async function linkedinInfo() { + const redirectProps = await linkedinRedirectProps() + if (!redirectProps) { + return null + } + + const redirectUrl = getRedirectUrl(redirectProps) + + return { + name: 'LinkedIn', + redirectUrl, + html: 'Sign in with your LinkedIn account' + } +} + +async function linkedinRedirectProps() { + try { + // extract well known url from env + const wellknownUrl = process.env.LINKEDIN_WELL_KNOWN_URL + if (!wellknownUrl) { + const message = 'LINKEDIN_WELL_KNOWN_URL is missing' + logger(`linkedinRedirectProps: ${message}`, 'error') + return null + } + + const authorization_endpoint = await getAuthEndpoint(wellknownUrl, 'linkedin') + if (!authorization_endpoint) { + const message = 'authorization_endpoint is missing' + logger(`linkedinRedirectProps: ${message}`, 'error') + return null + } + + const redirect_uri = process.env.LINKEDIN_REDIRECT + if (!redirect_uri) { + const message = 'LINKEDIN_REDIRECT is missing' + logger(`linkedinRedirectProps: ${message}`, 'error') + return null + } + + const client_id = process.env.LINKEDIN_CLIENT_ID + if (!client_id) { + const message = 'LINKEDIN_CLIENT_ID is missing' + logger(`linkedinRedirectProps: ${message}`, 'error') + return null + } + + const props: RedirectToProps = { + authorization_endpoint, + redirect_uri, + client_id, + scope: 'openid%20profile%20email', + response_mode: 'code' + } + return props + } catch (e: any) { + logger(`orcidRedirectProps: ${e.message}`, 'error') + return null + } +} diff --git a/frontend/pages/api/fe/auth/orcid.ts b/frontend/pages/api/fe/auth/orcid.ts index 0e57bf6..54facc1 100644 --- a/frontend/pages/api/fe/auth/orcid.ts +++ b/frontend/pages/api/fe/auth/orcid.ts @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: 2022 - 2024 Ewan Cahen (Netherlands eScience Center) -// SPDX-FileCopyrightText: 2022 - 2024 Netherlands eScience Center +// SPDX-FileCopyrightText: 2022 - 2025 Ewan Cahen (Netherlands eScience Center) +// SPDX-FileCopyrightText: 2022 - 2025 Netherlands eScience Center // SPDX-FileCopyrightText: 2022 Dusan Mijatovic (dv4all) // SPDX-FileCopyrightText: 2022 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences // SPDX-FileCopyrightText: 2022 Matthias Rüster (GFZ) @@ -16,18 +16,19 @@ // Next.js API route support: https://nextjs.org/docs/api-routes/introduction import type {NextApiRequest, NextApiResponse} from 'next' import logger from '~/utils/logger' -import {RedirectToProps, getAuthorisationEndpoint, getRedirectUrl} from '~/auth/api/authHelpers' +import {RedirectToProps, getRedirectUrl} from '~/auth/api/authHelpers' +import {getAuthEndpoint} from '~/auth/api/authEndpoint' import {Provider, ApiError} from '.' type Data = Provider | ApiError export async function orcidRedirectProps() { - try{ + try { // extract well known url from env const wellknownUrl = process.env.ORCID_WELL_KNOWN_URL ?? null if (wellknownUrl) { - // get (cached) authorisation endpoint from well known url - const authorization_endpoint = await getAuthorisationEndpoint(wellknownUrl) ?? null + // get (cached) authorisation endpoint from wellknown url + const authorization_endpoint = await getAuthEndpoint(wellknownUrl, 'orcid') ?? null if (authorization_endpoint) { // construct all props needed for redirectUrl const props: RedirectToProps = { @@ -49,14 +50,14 @@ export async function orcidRedirectProps() { logger(`orcidRedirectProps: ${message}`, 'error') return null } - }catch(e:any){ + } catch (e: any) { logger(`orcidRedirectProps: ${e.message}`, 'error') return null } } export async function orcidInfo() { - // extract all props from env and wellknow endpoint + // extract all props from env and wellknown endpoint const redirectProps = await orcidRedirectProps() if (redirectProps) { // create return url and the name to use in login button @@ -79,7 +80,7 @@ export default async function handler( res: NextApiResponse ) { try { - // extract all props from env and wellknow endpoint + // extract all props from env and wellknown endpoint // and create return url and the name to use in login button const loginInfo = await orcidInfo() if (loginInfo) { diff --git a/frontend/pages/api/fe/auth/surfconext.ts b/frontend/pages/api/fe/auth/surfconext.ts index 8311de1..1ac58c6 100644 --- a/frontend/pages/api/fe/auth/surfconext.ts +++ b/frontend/pages/api/fe/auth/surfconext.ts @@ -1,10 +1,10 @@ -// SPDX-FileCopyrightText: 2022 - 2023 Netherlands eScience Center +// SPDX-FileCopyrightText: 2022 - 2025 Ewan Cahen (Netherlands eScience Center) +// SPDX-FileCopyrightText: 2022 - 2025 Netherlands eScience Center // SPDX-FileCopyrightText: 2022 Dusan Mijatovic (dv4all) -// SPDX-FileCopyrightText: 2022 Ewan Cahen (Netherlands eScience Center) // SPDX-FileCopyrightText: 2022 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences // SPDX-FileCopyrightText: 2022 Matthias Rüster (GFZ) // SPDX-FileCopyrightText: 2022 dv4all -// SPDX-FileCopyrightText: 2023 Dusan Mijatovic (Netherlands eScience Center) +// SPDX-FileCopyrightText: 2023 - 2024 Dusan Mijatovic (Netherlands eScience Center) // // SPDX-License-Identifier: Apache-2.0 @@ -15,14 +15,15 @@ // Next.js API route support: https://nextjs.org/docs/api-routes/introduction import type {NextApiRequest, NextApiResponse} from 'next' -import {getAuthorisationEndpoint, RedirectToProps, getRedirectUrl} from '~/auth/api/authHelpers' import logger from '~/utils/logger' +import {RedirectToProps, getRedirectUrl} from '~/auth/api/authHelpers' +import {getAuthEndpoint} from '~/auth/api/authEndpoint' import {Provider, ApiError} from '.' type Data = Provider | ApiError const claims = { - id_token:{ + id_token: { schac_home_organization: null, name: null, email: null @@ -30,11 +31,11 @@ const claims = { } export async function surfconextRedirectProps() { - // extract wellknow url from env + // extract wellknown url from env const wellknownUrl = process.env.SURFCONEXT_WELL_KNOWN_URL ?? null if (wellknownUrl) { - // extract authorisation endpoint from wellknow response - const authorization_endpoint = await getAuthorisationEndpoint(wellknownUrl) + // get (cached) authorisation endpoint from wellknown url + const authorization_endpoint = await getAuthEndpoint(wellknownUrl, 'surfconext') ?? null if (authorization_endpoint) { // construct all props needed for redirectUrl const props: RedirectToProps = { @@ -59,7 +60,7 @@ export async function surfconextRedirectProps() { } export async function surfconextInfo() { - // extract all props from env and wellknow endpoint + // extract all props from env and wellknown endpoint const redirectProps = await surfconextRedirectProps() if (redirectProps) { // create return url and the name to use in login button @@ -80,7 +81,7 @@ export default async function handler( res: NextApiResponse ) { try { - // extract all props from env and wellknow endpoint + // extract all props from env and wellknown endpoint // and create return url and the name to use in login button const loginInfo = await surfconextInfo() if (loginInfo) {