Merge pull request #450 from Safelite/feature/SSR-705

SSR-705: Client signature validation call implementation
This commit is contained in:
Jeremy-Z 2023-09-22 13:07:54 -04:00 committed by GitHub
commit 829bd75eb2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 68 additions and 30 deletions

View file

@ -126,6 +126,10 @@ const endpoints = Object.freeze({
url: '/clientauth/api/v1/clientauth/validate-client-tag', url: '/clientauth/api/v1/clientauth/validate-client-tag',
method: 'GET' method: 'GET'
}, },
ValidateClientSignature: {
url: '/clientauth/api/v1/clientauth/validate-client-signature',
method: 'POST'
},
IsVinbyAddressPermissible: { IsVinbyAddressPermissible: {
url: '/vehicle/api/v1/vehicle/is-vin-by-address-permissible', url: '/vehicle/api/v1/vehicle/is-vin-by-address-permissible',
method: 'Get' method: 'Get'

View file

@ -1,6 +1,6 @@
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
const validateISSClientTag = (clientTag) => { export function validateISSClientTag(clientTag) {
const store = useMainStore(); const store = useMainStore();
return store.validateClientTag(clientTag) return store.validateClientTag(clientTag)
@ -8,6 +8,14 @@ const validateISSClientTag = (clientTag) => {
(response) => response, (response) => response,
() => null () => null
); );
}; }
export default validateISSClientTag; export function validateISSClientSignature(clientTag, token, signature) {
const store = useMainStore();
return store.validateClientSignature(clientTag, token, signature)
.then(
(response) => response,
() => null
);
}

View file

@ -9,7 +9,7 @@
<script> <script>
// Supporting files // Supporting files
import issPageValues from '@/router/router-constants/issPage-values'; import issPageValues from '@/router/router-constants/issPage-values';
import validateISSClientTag from '@/helpers/clientauth-helper'; import { validateISSClientTag, validateISSClientSignature } from '@/helpers/clientauth-helper';
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
export default { export default {
@ -44,10 +44,13 @@ export default {
parseQueryParms() { parseQueryParms() {
// Dump the query string parameters into an array. Remove casing on the key for easy compare. // Dump the query string parameters into an array. Remove casing on the key for easy compare.
const queryStringParams = []; const queryStringParams = [];
// TODO: Modify to not iterate entire prototype chain
for (const param in this.$route.query) { if (this.$route?.query) {
queryStringParams[param.toLowerCase()] = this.$route.query[param]; Object.keys(this.$route.query).forEach((param) => {
queryStringParams[param.toLowerCase()] = this.$route.query[param];
});
} }
return queryStringParams; return queryStringParams;
}, },
async validateClientTagOnEntry() { async validateClientTagOnEntry() {
@ -60,13 +63,27 @@ export default {
const resp = await validateISSClientTag(clientTag); const resp = await validateISSClientTag(clientTag);
if (resp?.data.active && resp?.data.accountName.length > 0) { if (resp?.data.active && resp?.data.accountName.length > 0) {
this.populateISSConfigValues(resp.data); if (resp.data.authentication === 'RSAToken') {
const token = queryStringParams.token;
const signature = queryStringParams.signature;
authorized = true; const vsigResp = await validateISSClientSignature(clientTag, token, signature);
this.mainStore.issConfig.isAuthenticated = vsigResp?.data?.valid ?? false;
if (resp.data.parameters?.length > 0) { if (this.mainStore.issConfig.isAuthenticated) {
const finalParams = this.combineClientParameters(resp.data.parameters, queryStringParams); authorized = true;
this.populateStoreItemsFromParams(finalParams); }
} else {
authorized = true;
}
if (authorized) {
await this.populateISSConfigValues(resp.data);
if (resp.data.parameters?.length > 0) {
const finalParams = this.combineClientParameters(resp.data.parameters, queryStringParams);
this.populateStoreItemsFromParams(finalParams);
}
} }
} }
} }
@ -78,17 +95,13 @@ export default {
window.location = `/?issPage=${issPageValues.WELCOME_PAGE}`; window.location = `/?issPage=${issPageValues.WELCOME_PAGE}`;
} }
}, },
populateISSConfigValues(data) { async populateISSConfigValues(data) {
this.mainStore.issConfig.clientName = data.accountName; this.mainStore.issConfig.clientName = data.accountName;
this.mainStore.issConfig.clientDisplayName = data.accountName; // Defaults to use the client name. this.mainStore.issConfig.clientDisplayName = data.accountName; // Defaults to use the client name.
this.mainStore.issConfig.accountNumber = data.accountNumber; this.mainStore.issConfig.accountNumber = data.accountNumber;
this.mainStore.issConfig.styleSheet = data.styleSheet; this.mainStore.issConfig.styleSheet = data.styleSheet;
this.mainStore.issConfig.isCoverageEnabled = data.coverageEnabled; this.mainStore.issConfig.isCoverageEnabled = data.coverageEnabled;
// NOTE: This is only here for testing purposes.
// Will be removed and replaced by actual token / signature validation when work is completed.
if (data.authentication === 'RSAToken') this.mainStore.issConfig.isAuthenticated = true;
try { try {
if (data.clientFlags) { if (data.clientFlags) {
const clientFlags = JSON.parse(data.clientFlags); const clientFlags = JSON.parse(data.clientFlags);
@ -115,19 +128,17 @@ export default {
try { try {
const clientParams = JSON.parse(configParams); const clientParams = JSON.parse(configParams);
// TODO: Modify to not iterate entire prototype chain Object.keys(clientParams).forEach((cparam) => {
for (const cparam in clientParams) {
const cname = clientParams[cparam].toLowerCase(); const cname = clientParams[cparam].toLowerCase();
// TODO: Modify to not iterate entire prototype chain Object.keys(queryStringParams).forEach((qsparam) => {
for (const qsparam in queryStringParams) {
const qsname = qsparam.toLowerCase(); const qsname = qsparam.toLowerCase();
if (cname === qsname) { if (cname === qsname) {
finalParams[cname] = queryStringParams[cname]; finalParams[cname] = queryStringParams[cname];
} }
} });
} });
} catch (e) { } catch (e) {
window.console.error(`Error combining client parameters: ${e}`); window.console.error(`Error combining client parameters: ${e}`);
} }
@ -136,8 +147,8 @@ export default {
}, },
populateStoreItemsFromParams(params) { populateStoreItemsFromParams(params) {
// Populate store items from parameters. // Populate store items from parameters.
// TODO: Modify to not iterate entire prototype chain
for (const param in params) { Object.keys(params).forEach((param) => {
const name = param.toLowerCase(); const name = param.toLowerCase();
const value = params[param]; const value = params[param];
@ -172,7 +183,7 @@ export default {
break; break;
default: default:
} }
} });
} }
} }
}; };

View file

@ -344,7 +344,7 @@ export const useMainStore = defineStore({
getDamageOptions(carId) { getDamageOptions(carId) {
return globalMethods.callHttpClient({ return globalMethods.callHttpClient({
methods: endpoints.GetDamageOptions.method, method: endpoints.GetDamageOptions.method,
endpoint: `${endpoints.GetDamageOptions.url}/${carId}`, endpoint: `${endpoints.GetDamageOptions.url}/${carId}`,
payload: {} payload: {}
}); });
@ -799,7 +799,7 @@ export const useMainStore = defineStore({
setVehicle() { setVehicle() {
return globalMethods return globalMethods
.callHttpClient({ .callHttpClient({
methods: endpoints.GetVehicle.method, method: endpoints.GetVehicle.method,
endpoint: `${endpoints.GetVehicle.url}/${this.order.vehicle.year}/${this.order.vehicle.make}/${this.order.vehicle.model}/${this.order.vehicle.style}`, endpoint: `${endpoints.GetVehicle.url}/${this.order.vehicle.year}/${this.order.vehicle.make}/${this.order.vehicle.model}/${this.order.vehicle.style}`,
payload: {} payload: {}
}) })
@ -1535,18 +1535,33 @@ export const useMainStore = defineStore({
async validateZip({ zip }) { async validateZip({ zip }) {
return await globalMethods.callHttpClient({ return await globalMethods.callHttpClient({
methods: endpoints.ValidateZip.method, method: endpoints.ValidateZip.method,
endpoint: `${endpoints.ValidateZip.url}/${zip}` endpoint: `${endpoints.ValidateZip.url}/${zip}`
}); });
}, },
async validateClientTag(clientTag) { async validateClientTag(clientTag) {
return await globalMethods.callHttpClient({ return await globalMethods.callHttpClient({
methods: endpoints.ValidateClientTag.method, method: endpoints.ValidateClientTag.method,
endpoint: `${endpoints.ValidateClientTag.url}/${clientTag}` endpoint: `${endpoints.ValidateClientTag.url}/${clientTag}`
}); });
}, },
async validateClientSignature(clientTag, token, signature) {
const payload = {
clientTag,
token,
signature
};
return await globalMethods.callHttpClient({
method: endpoints.ValidateClientSignature.method,
endpoint: endpoints.ValidateClientSignature.url,
payload
});
},
saveServiceLocation(serviceLocationInfo) { saveServiceLocation(serviceLocationInfo) {
if (this.order.serviceLocation) { if (this.order.serviceLocation) {
if ( if (