diff --git a/packages/pn-personagiuridica-webapp/src/__mocks__/Auth.mock.ts b/packages/pn-personagiuridica-webapp/src/__mocks__/Auth.mock.ts index 0f35237333..62142564b5 100644 --- a/packages/pn-personagiuridica-webapp/src/__mocks__/Auth.mock.ts +++ b/packages/pn-personagiuridica-webapp/src/__mocks__/Auth.mock.ts @@ -2,14 +2,14 @@ import MockAdapter from 'axios-mock-adapter'; import { authClient } from '../api/apiClients'; import { AUTH_TOKEN_EXCHANGE } from '../api/auth/auth.routes'; -import { exchangeToken, logout } from '../redux/auth/actions'; import { PNRole, PartyRole, User } from '../models/User'; +import { exchangeToken, logout } from '../redux/auth/actions'; import { store } from '../redux/store'; export const mockLogin = async (body: User | string = userResponse): Promise => { const mock = new MockAdapter(authClient); mock.onPost(AUTH_TOKEN_EXCHANGE(), { authorizationToken: 'mocked-token' }).reply(200, body); - const action = store.dispatch(exchangeToken('mocked-token')); + const action = store.dispatch(exchangeToken({ spidToken: 'mocked-token' })); mock.reset(); mock.restore(); return action; @@ -23,7 +23,7 @@ export const mockAuthentication = () => { beforeAll(() => { mock = new MockAdapter(authClient); mock.onPost(AUTH_TOKEN_EXCHANGE()).reply(200, userResponse); - store.dispatch(exchangeToken('mocked-token')); + store.dispatch(exchangeToken({ spidToken: 'mocked-token' })); }); afterAll(() => { @@ -61,3 +61,11 @@ export const userResponse: User = { }, hasGroup: false, }; + +export const userResponseWithSource: User = { + ...userResponse, + source: { + channel: 'WEB', + details: 'QR_CODE', + }, +}; diff --git a/packages/pn-personagiuridica-webapp/src/api/auth/Auth.api.ts b/packages/pn-personagiuridica-webapp/src/api/auth/Auth.api.ts index f470c3d4ce..268ed24f27 100644 --- a/packages/pn-personagiuridica-webapp/src/api/auth/Auth.api.ts +++ b/packages/pn-personagiuridica-webapp/src/api/auth/Auth.api.ts @@ -1,31 +1,44 @@ -import { User } from '../../models/User'; +import { TokenExchangeBody, User } from '../../models/User'; import { authClient } from '../apiClients'; import { AUTH_TOKEN_EXCHANGE } from './auth.routes'; export const AuthApi = { - exchangeToken: (spidToken: string): Promise => - authClient - .post(AUTH_TOKEN_EXCHANGE(), { authorizationToken: spidToken }) - .then((response) => ({ - sessionToken: response.data.sessionToken, - email: response.data.email, - name: response.data.name, - family_name: response.data.family_name, - uid: response.data.uid, - fiscal_number: response.data.fiscal_number, - from_aa: response.data.from_aa, - aud: response.data.aud, - level: response.data.level, - iat: response.data.iat, - exp: response.data.exp, - iss: response.data.iss, - jti: response.data.jti, - organization: response.data.organization, - desired_exp: response.data.desired_exp, - hasGroup: Boolean( - response.data.organization && - response.data.organization.groups && - response.data.organization.groups.length > 0 - ), - })), + exchangeToken: async (spidToken: string, aar?: string): Promise => { + const body: TokenExchangeBody = { authorizationToken: spidToken }; + if (aar) { + // eslint-disable-next-line functional/immutable-data + body.source = { + type: 'QR', + id: aar, + }; + } + const response = await authClient.post(AUTH_TOKEN_EXCHANGE(), body); + const user: User = { + sessionToken: response.data.sessionToken, + email: response.data.email, + name: response.data.name, + family_name: response.data.family_name, + uid: response.data.uid, + fiscal_number: response.data.fiscal_number, + from_aa: response.data.from_aa, + aud: response.data.aud, + level: response.data.level, + iat: response.data.iat, + exp: response.data.exp, + iss: response.data.iss, + jti: response.data.jti, + organization: response.data.organization, + desired_exp: response.data.desired_exp, + hasGroup: Boolean( + response.data.organization && + response.data.organization.groups && + response.data.organization.groups.length > 0 + ), + }; + if (aar && response.data.source) { + /* eslint-disable-next-line functional/immutable-data */ + user.source = response.data.source; + } + return user; + }, }; diff --git a/packages/pn-personagiuridica-webapp/src/api/auth/__test__/Auth.api.test.ts b/packages/pn-personagiuridica-webapp/src/api/auth/__test__/Auth.api.test.ts index 034a64614a..df829a2e3c 100644 --- a/packages/pn-personagiuridica-webapp/src/api/auth/__test__/Auth.api.test.ts +++ b/packages/pn-personagiuridica-webapp/src/api/auth/__test__/Auth.api.test.ts @@ -1,18 +1,35 @@ import MockAdapter from 'axios-mock-adapter'; -import { userResponse } from '../../../__mocks__/Auth.mock'; +import { userResponse, userResponseWithSource } from '../../../__mocks__/Auth.mock'; import { authClient } from '../../apiClients'; import { AuthApi } from '../Auth.api'; import { AUTH_TOKEN_EXCHANGE } from '../auth.routes'; describe('Auth api tests', () => { + let mock: MockAdapter; + + beforeAll(() => { + mock = new MockAdapter(authClient); + }); + + afterEach(() => { + mock.reset(); + }); + it('exchangeToken', async () => { const token = 'mocked-token'; - const mock = new MockAdapter(authClient); mock.onPost(AUTH_TOKEN_EXCHANGE(), { authorizationToken: token }).reply(200, userResponse); const res = await AuthApi.exchangeToken(token); expect(res).toStrictEqual(userResponse); - mock.reset(); - mock.restore(); + }); + + it('exchangeToken with aar', async () => { + const token = 'mocked-token'; + const aar = 'mock-aar'; + mock + .onPost(AUTH_TOKEN_EXCHANGE(), { authorizationToken: token, source: { type: 'QR', id: aar } }) + .reply(200, userResponseWithSource); + const res = await AuthApi.exchangeToken(token, aar); + expect(res).toStrictEqual(userResponseWithSource); }); }); diff --git a/packages/pn-personagiuridica-webapp/src/models/User.ts b/packages/pn-personagiuridica-webapp/src/models/User.ts index 7417d50234..1670b596ee 100644 --- a/packages/pn-personagiuridica-webapp/src/models/User.ts +++ b/packages/pn-personagiuridica-webapp/src/models/User.ts @@ -36,4 +36,18 @@ export interface User extends BasicUser { organization: Organization; desired_exp: number; hasGroup?: boolean; -} \ No newline at end of file + source?: UserSource; +} + +export interface UserSource { + channel: 'WEB'; + details: string; // 'QR_CODE'; +} + +export interface TokenExchangeBody { + authorizationToken: string; + source?: { + type: 'QR'; + id: string; + }; +} diff --git a/packages/pn-personagiuridica-webapp/src/navigation/SessionGuard.tsx b/packages/pn-personagiuridica-webapp/src/navigation/SessionGuard.tsx index e1b210b9ce..8a4e3a9d04 100644 --- a/packages/pn-personagiuridica-webapp/src/navigation/SessionGuard.tsx +++ b/packages/pn-personagiuridica-webapp/src/navigation/SessionGuard.tsx @@ -44,7 +44,6 @@ const manageUnforbiddenError = (e: any) => { return true; }; - // Perché ci sono due componenti. // Il codice in SessionGuard implementa i steps necessari per determinare se c'è sessione, se è utente abilitato, se è sessione anonima, ecc.. // D'altra parte, SessionGuardRender implementa la logica di cosa si deve renderizzare, @@ -174,8 +173,9 @@ const SessionGuard = () => { // ---------------------- const spidToken = getTokenParam(); if (spidToken) { + const aar = localStorage.getItem(AppRouteParams.AAR) || undefined; AppResponsePublisher.error.subscribe('exchangeToken', manageUnforbiddenError); - await dispatch(exchangeToken(spidToken)); + await dispatch(exchangeToken({ spidToken, aar })); } }; void performStep(INITIALIZATION_STEPS.USER_DETERMINATION, doUserDetermination); diff --git a/packages/pn-personagiuridica-webapp/src/redux/auth/actions.ts b/packages/pn-personagiuridica-webapp/src/redux/auth/actions.ts index 71c22edc41..d53e593267 100644 --- a/packages/pn-personagiuridica-webapp/src/redux/auth/actions.ts +++ b/packages/pn-personagiuridica-webapp/src/redux/auth/actions.ts @@ -18,11 +18,11 @@ export enum AUTH_ACTIONS { * Exchange token action between selfcare and pn. * If token is valid, user info are set in sessionStorage */ -export const exchangeToken = createAsyncThunk( +export const exchangeToken = createAsyncThunk( 'exchangeToken', - async (spidToken, { rejectWithValue }) => { + async ({ spidToken, aar }, { rejectWithValue }) => { try { - return await AuthApi.exchangeToken(spidToken); + return await AuthApi.exchangeToken(spidToken, aar); } catch (e: any) { return rejectWithValue(parseError(e)); } diff --git a/packages/pn-personagiuridica-webapp/src/redux/auth/reducers.ts b/packages/pn-personagiuridica-webapp/src/redux/auth/reducers.ts index eb61d2efee..5b7598f778 100644 --- a/packages/pn-personagiuridica-webapp/src/redux/auth/reducers.ts +++ b/packages/pn-personagiuridica-webapp/src/redux/auth/reducers.ts @@ -39,6 +39,12 @@ const userDataMatcher = yup organization: organizationMatcher, desired_exp: yup.number(), hasGroup: yup.boolean(), + source: yup + .object({ + channel: yup.string().oneOf(['WEB']), + details: yup.string(), + }) + .optional(), }) .noUnknown(true);