Skip to content

Commit

Permalink
feat(pn-13895): [PG] pass source AAR in exchangeToken (#1476)
Browse files Browse the repository at this point in the history
  • Loading branch information
fbianchicodermine authored Feb 24, 2025
1 parent 96accd9 commit 5f2da71
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 39 deletions.
14 changes: 11 additions & 3 deletions packages/pn-personagiuridica-webapp/src/__mocks__/Auth.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<any> => {
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;
Expand All @@ -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(() => {
Expand Down Expand Up @@ -61,3 +61,11 @@ export const userResponse: User = {
},
hasGroup: false,
};

export const userResponseWithSource: User = {
...userResponse,
source: {
channel: 'WEB',
details: 'QR_CODE',
},
};
65 changes: 39 additions & 26 deletions packages/pn-personagiuridica-webapp/src/api/auth/Auth.api.ts
Original file line number Diff line number Diff line change
@@ -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<User> =>
authClient
.post<User>(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<User> => {
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<User>(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;
},
};
Original file line number Diff line number Diff line change
@@ -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);
});
});
16 changes: 15 additions & 1 deletion packages/pn-personagiuridica-webapp/src/models/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,18 @@ export interface User extends BasicUser {
organization: Organization;
desired_exp: number;
hasGroup?: boolean;
}
source?: UserSource;
}

export interface UserSource {
channel: 'WEB';
details: string; // 'QR_CODE';
}

export interface TokenExchangeBody {
authorizationToken: string;
source?: {
type: 'QR';
id: string;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions packages/pn-personagiuridica-webapp/src/redux/auth/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<User, string>(
export const exchangeToken = createAsyncThunk<User, { spidToken: string; aar?: string }>(
'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));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down

0 comments on commit 5f2da71

Please sign in to comment.