Onderzoeken hoe geavanceerde cybersecurity beveiligingstechnologieën geïntegreerd kunnen worden in de data oplossing zodat we data veilig kunnen delen (dataoverdracht) middels een geavanceerd en merk onafhankelijk Big Data platform / productfeed infrastructuur van GMU, met behoud van prestaties, compatibiliteit met advertentieplatforms en schaalbaarheid. De focus ligt op het beschermen van klantdata, concurrentiegevoelige informatie en het waarborgen van compliance met AVG/GDPR.
De scope van deze haalbaarheidsstudie blijft beperkt tot de feedmanager klantdata binnen de GMU marketing systemen. Dit betreft uitsluitend de gestructureerde productfeeddata die door GMU wordt beheerd, verwerkt en doorgestuurd naar advertentieplatformen zoals Google Shopping, Meta en vergelijkingssites.
Het systeem waarop deze data wordt verwerkt en beheerd is een PHP-backend applicatie, die fungeert als centrale schakel tussen klantinput, feedverrijking en output naar externe advertentiekanalen. De studie richt zich op de cyberweerbaarheid van dit ecosysteem, inclusief:
Data-invoer vanuit klanten via API’s, CSV’s of CMS-koppelingen
Verwerking, verrijking en transformatie van feeddata binnen het PHP-systeem
Opslag van feeddata (tijdelijk of persistent)
Externe ontsluiting van feeds richting advertentieplatformen via API’s, XML of JSON Interne toegang tot feeddata door medewerkers en developers via test- of stagingomgevingen
Niet binnen scope vallen:
CRM-data of analyticsdata buiten de feedmanager
Front-end applicaties, klantportalen of marketing dashboards Backend componenten van derden die buiten GMU-beheer vallen
Het doel is om de beveiliging en privacy van de feedmanager-data te versterken zonder de operationele continuïteit, schaalbaarheid of platformcompatibiliteit aan te tasten.
Compliance scope
Algemene Verordening Gegevensbescherming (AVG / GDPR) Toepasbaar omdat:
De feedmanager persoonsgegevens bevat of kan bevatten (zoals productgerelateerde data met identificeerbare elementen). GMU optreedt als verwerker en/of verwerkingsverantwoordelijke.
Belangrijke vereisten:
Rechtmatigheid, doelbinding en dataminimalisatie: Alleen data verwerken die noodzakelijk is voor het doel.
Beveiliging (Art. 32): Technische en organisatorische maatregelen om datalekken en ongeautoriseerde toegang te voorkomen (versleuteling, pseudonimisering).
Data Protection by Design & Default: Beveiliging moet ingebouwd zijn in het ontwerp van systemen. Verwerkersovereenkomsten: Duidelijke afspraken met klanten en externe partners over de omgang met data. Datalekmeldplicht: Verplichting om incidenten binnen 72 uur te melden aan de toezichthouder (Autoriteit Persoonsgegevens).
Onderzoeksdoel
Bepalen of het toepassen van end-to-end encryptie (zoals AES-256 of homomorfe encryptie) haalbaar is voor productfeeddata zonder negatieve impact op de verwerkingssnelheid of datakwaliteit.
Onderzoeksvragen
Welke encryptiestandaarden zijn geschikt voor gestructureerde productfeeddata?
Wat is de impact van encryptie op realtime verwerking en feed-updates?
Hoe kan sleutelbeheer veilig en schaalbaar worden ingericht?
Hoe kan er een Cleanroom met schone, veilige & betrouwbare data gecreerd worden?
Hoe kan er een example database gecreerd worden voor een ieder die niet bij de cleanroom data hoeft (zoals developers).
Go / No Go vereisten
Go = De mogelijkheid van het creeren van een data saferoom met encriptie zonder extreme latency, ook is compatibiliteit met advertentie systemen belangrijk. No Go = geen compatibiliteit of extreme vertraging.
Beoordelen of SMPC inzetbaar is om data-analyses mogelijk te maken waarbij meerdere partijen samenwerken met versleutelde data, zonder dat individuele invoer zichtbaar wordt voor anderen.
Onderzoeksvragen
Voor welke feedcomponenten is SMPC relevant (bijv. prijsstrategie of voorraad)?
Wat is de performance-impact van SMPC in een productieomgeving?
Hoe verhoudt SMPC zich tot andere methoden qua schaalbaarheid en dataconsistentie?
Welke bestaande SMPC-frameworks zijn geschikt voor integratie binnen de GMU-infrastructuur?
Hoe kunnen resultaten bruikbaar blijven voor analytics en optimalisaties zonder de onderliggende data te onthullen?
Go / No Go vereisten
Go = Bewezen toepasbaarheid van SMPC met werkbare performance en bruikbare output voor advertentie-optimalisatie. No Go = Te hoge performancekosten, onbruikbare resultaten of te complexe implementatie voor structureel gebruik.
Onderzoeksdoel
Ontwikkelen van een data parsing engine die automatisch gevoelige velden maskeert of anonimiseert, terwijl de feed functioneel blijft voor externe platformen.
Onderzoeksvragen
Welke datavelden moeten structureel gemaskeerd worden op basis van gevoeligheid?
Is dynamische of statische masking het meest geschikt voor continue feed-updates?
Hoe blijft compatibiliteit met externe systemen zoals Google Merchant Center en Meta behouden?
Hoe kan masking zodanig worden geïmplementeerd dat ontwikkelaars toegang houden tot testbare feeds?
Kan er een loggingmechanisme worden ingebouwd dat inzicht geeft in gemaskeerde datapunten zonder herleidbaarheid?
Go / No Go vereisten
Go = Gevoelige data wordt afdoende gemaskeerd met behoud van functionele feedintegriteit en compatibiliteit.
No Go = Gegevensmaskering leidt tot fouten in platformacceptatie of verlies van noodzakelijke feedfunctionaliteit.
Onderzoeken of het mogelijk is om data op te schonen middels data sanitization algoritmen en eventueel modellen te trainen op klantdata zonder dat deze data centraal opgeslagen wordt, door gebruik te maken van data sanitization en federated learning.
Onderzoeksvragen
In hoeverre zijn feeddata en prestatiegegevens bruikbaar voor lsanitization?
Welke data sanitizations zijn toepasbaar en schaalbaar binnen de GMU-context?
Kan data effectief geschoond worden van PII (persoonlijk identificeerbare informatie) zonder verlies van waardevolle signalen?
Is een een sanitization-pipeline (inclusief detectie en verwijdering van PII) haalbaar binnen de klantdata feedmanager?
Welke klanten en use cases zijn het meest geschikt voor een pilot?
Is fedrated learning een waardevolle toepassing? Hoe worden modelupdates veilig teruggestuurd en samengevoegd tot een globaal model?
Go / No Go vereisten
Go = Data sanitizations leidt tot betere data & een hogere mate van veiligheid. No Go = Data sanitization verwijderd correcte waardevolle data.
Onderzoeksdoel
Beoordelen en versterken van de API-beveiliging binnen de feedverwerkingsketen, met aandacht voor toegangsbeheer, encryptie, logging en throttling.
Onderzoeksvragen
Welke API’s zijn betrokken bij feedverwerking en -uitwisseling?
Welke securitymechanismen zijn afdoende voor gevoelige toepassingen?
Hoe kan het gebruik van een API Gateway bijdragen aan betere toegangscontrole en monitoring?
Wat zijn de beste technieken voor rate-limiting, request validation en logging?
Hoe kunnen API’s veilig beschikbaar zijn voor derde partijen (bijv. adverteerders of developers) zonder risico? Hoe kunnen we de API’s voldoende beveiligen zonder een te grote vertraging in data te creeren?
Kunnen we naast de data saveroom ook example demo data genereren voor developers zonder risico?
Go / No Go vereisten
Go = Alle API’s voldoen aan moderne beveiligingsnormen en zijn aantoonbaar beschermd tegen misbruik of datalekken.
No Go = Kritieke kwetsbaarheden, onveilige toegangsstructuren of incompatibiliteit met securityrichtlijnen van partners.
nderzoek naar bestaande big data oplossingen (concurrentie), technologieën en tools op het gebied van gegevensbeveiliging zodat we data veilig kunnen delen en cybersecurity.
Het bestaande aanbod rond “privacy‑enhancing technologies” (PET’s) voor product‑feeds groeit snel, wij hebben hierbij onderzocht:
Criteria: performance (latency per feed‑call), compatibiliteit met Google Shopping/Meta, EU GDPR compliance, schaalbaarheid naar 1k+ klanten.
Data‑clean‑rooms van:
Snowflake
Google Ads Data Hub
AWS Clean Rooms
homomorfe‑encryptiebibliotheken zoals Microsoft SEAL / OpenFHE
API‑gateways zoals Kong.
Toch blijkt geen enkele oplossing tegelijk PHP‑native, feed‑specifiek én afgestemd op near‑real‑time advertentie‑platformen.
Onze analyse hieronder laat zien dat een eigen module binnen de GM‑feedmanager zowel technisch haalbaar als strategisch aantrekkelijk is, vooral omdat het:
In de zoektocht naar een schaalbare, realtime en privacy bewuste architectuur onderscheiden we vijf sleutelgebieden.
Onderstaand per onderdeel een opsomming van de beschikbare (mogelijk geschikte) oplossingen, incl. belangrijkste voordelen + de belangrijkste beperkingen waar je rekening mee moet houden.
1. Data cleanroom & encryptie
Hier draait het om veilige, gedeelde analyses zonder ruwe gegevens bloot te geven.
Externe oplossingen
Snowflake Clean Room | (https://www.snowflake.com/trending/data-clean-room-for-business-growth/)
AWS Clean Rooms | (https://docs.aws.amazon.com/clean-rooms/latest/userguide/data-protection.html)
AppsFlyer CDR | (https://www.appsflyer.com/resources/guides/data-clean-rooms/)
Sterke punten
Volledig key management voor je encryptiesleutels
Strikte query governance: wie wat mag opvragen, is volledig controleerbaar
Gebruiksvriendelijk via SaaS interface
Zwakke punten ten opzichte van GMU-eisen
Vendor lock in: migreren of switchen is complex
Aanzienlijke kosten per TB data
Geen PHP SDK beschikbaar, extra ontwikkelingswerk nodig
Feedlatentie van 0,5–2 s zal analyses vertragen
Alternatief: FHE-libraries (SEAL, OpenFHE) | https://eprint.iacr.org/2025/473.pdf
Voordeel: end to end versleuteling, data blijft altijd encrypted
Nadeel: tot wel 100× hogere CPU overhead; niet geschikt voor live-feeds
2. Secure Multi Party Computation (SMPC)
Ideaal voor de berekeningen op gedeelde data zonder dat partijen hun eigen gegevens prijsgeven.
Externe oplossingen
Sharemind | (https://sharemind.cyber.ee/sharemind-mpc/multi-party-computation)
Partisia MPC | (https://medium.com/partisia-blockchain/august-2024-update-bf35f6f90799)
Sterke punten
Volwassen, out of the box stacks met beveiligingsaudits
Ondersteuning voor complexe berekeningen over versleutelde data
Zwakke punten ten opzichte van GMU-eisen
In C++/Rust ontwikkeld; ontbrekende PHP SDK
Runtime overhead van 5× 20× vraagt extra infrastructuur
Latentie en resource-gebruik groeiend bij schaalvergroting
3. Data masking engine
Verberg of anonimiseer (PII) velden voordat data wordt gedeeld of geladen.
Externe oplossingen
Microsoft Presidio | (https://github.com/microsoft/presidio)
Talend DataMasker | (https://tsh.io/blog/fogger-open-source-free-tool-gdpr-data-masking)
Fogger (OSS) | (https://medium.com/%40ricardo.a.nolan/protecting-personal-data-in-laravel-how-to-mask-and-unmask-customer-information-while-staying-gdpr-82d96684cf2b)
Sterke punten
Automatische PII detectie (namen, adressen, telefoonnummers)
Flexibele, regel gebaseerde masking-regels
Open source opties voor maatwerk
Zwakke punten ten opzichte van GMU-eisen
Geen built-in validatie voor Google Merchant-feeds
Vereist een aanvullende microservice om feed-compliance te waarborgen
4. Sanitization & Federated Learning
Combineert opschonen van data met gedistribueerde modeltraining zonder centrale dataset.
Externe oplossingen
Presidio scrub pipelines
TensorFlow Federated (TFF) | ( https://www.tensorflow.org/federated/faq)
Flower | https://flower.ai/
Sterke punten
Krachtige algoritmen om ongewenste content te saniteren
Federated Learning-orchestratie: modellen leren op lokale data zonder delen
Zwakke punten ten opzichte van GMU-eisen
TFF is nog niet volledig productie-klaar
Flower ondersteunt alleen Python; PHP-integratie vereist gRPC
Extra ontwikkel- en onderhoudslast voor koppelingen
5. API beveiliging
De poortwachter tussen partners, klanten en jouw GMU-stroom.
Externe oplossingen
Kong | https://docs.konghq.com/gateway/latest/get-started/rate-limiting/ | https://medium.com/%40aditya541/mastering-api-management-with-kong-gateway-a-practical-guide-72e6f54997de
Tyk
Apigee
AWS API Gateway
Sterke punten
Ondersteuning voor mTLS en OAuth2-flows
Rate Limiting en quota beheer via plugins
Out of the box securitypattern implementaties
Zwakke punten ten opzichte van GMU eisen
Extra hop: introduceert onvermijdelijke latency
Licentiemodellen gebaseerd op request aantallen kunnen kostbaar worden
|
Factor |
Buy |
Build (GMU PHP module) |
|
Capex / 3 jaar |
€ 300k–€ 650k licenties + egress |
c.a. € 150k R&D |
|
Opex / maand |
Variabel (API calls, GB‑ops) |
< € 200 per maand |
|
Latency |
500 ms 2 s |
30 80 ms (local) |
|
Custom logic |
Gelimiteerd, afhankelijk van vendor roadmap |
Volledig in house |
|
Vendor‑lock‑in |
Hoog |
Laag |
|
GDPR‑controle |
Shared responsibility |
Volledige controle |
|
Strategische |
Pariteit met concurrent |
Differentiatie (security first feed manager) |
Conclusie Marktonderzoek:
Er is momenteel geen externe oplossing die tegelijk PHP native, feedspecifiek, near realtime en volledig AVG‑compliant is voor productfeeds. Externe PET platformen brengen hogere latency, licentiekosten en vendorlock‑in met zich mee.
Door zelf een beveiligingsmodule in de bestaande GMU PHPstack te bouwen, kan GMU:
Conclusie van het marktonderzoek is daarmee dat zelf een proof of concept voor onze big data oplossing middels een geavanceerd en merk onafhankelijk platform technisch haalbaar, economisch voordelig en strategisch de beste route is.
Implementeren van versleutelde opslag en transport van data waarbij enkel bevoegden toegang krijgen via decryptiesleutels.
Meerdere partijen in staat stellen om berekeningen uit te voeren op gecodeerde datasets zonder individuele datasets prijs te geven.
Automatische verwerking en anonimiseren van data zodat individuele identiteiten niet te herleiden zijn.
Opschonen van data en toepassen van machine learning zonder centrale data-opslag.
Veilige data-uitwisseling tussen applicaties via geavanceerde API-beveiliging.
Met gerichte training, voldoende R&D tijd, goede voorbereiding en investering in expertise zijn alle voorgestelde oplossingen technisch haalbaar en effectief te implementeren binnen de bestaande infrastructuur.
Prototyping/iteratieve ontwikkeling
Resultaten: In een periode van vier maanden hebben zes medewerkers, in nauwe samenwerking met externe experts, significante resultaten geboekt die GMU BV in staat stellen om veilig en privacybeschermend met externe data te werken. Allereerst is een werkend prototype ontwikkeld van het AI-gedreven Marketing as a Service (MaaS) platform, waarin geavanceerde databeveiligingstechnieken zoals end-to-end encryptie en Secure Multi-Party Computation (MPC) zijn geïntegreerd. Dit prototype is iteratief verbeterd, waarbij elke ontwikkelingscyclus werd gevolgd door grondige tests en feedbacksessies.
Onderstaand ter inspiratie een aantal (beperkte) snippets van deze code.
Wenst u hier meer over te weten, dan kunt u contact met ons opnemen via www.GMU.online
<div
class="@if($multipleSearchDomains) bg-slate-50 rounded-md border border-gray-300/75 p-4 @endif">
@if($multipleSearchDomains)
<div class="font-semibold text-lg text-gray-700 my-5">Searching:
({{count($multipleSearchDomains)}})
</div>
<div class="flex flex-wrap">
@foreach($multipleSearchDomains as $domain)
<p class="font-semibold bg-indigo-50 rounded border border-indigo-300/75 py-1.5 px-2 my-1.5 mx-1.5">
{{$domain}}
<button wire:click="removeSearchDomain('{{$domain}}')"
class="ml-0.5 inline-flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full text-indigo-500 hover:bg-indigo-300 hover:text-indigo-700 focus:bg-indigo-500 focus:text-white focus:outline-none">
<svg class="h-2 w-2" stroke="currentColor" fill="none" viewBox="0 0 8 8">
<path stroke-linecap="round" stroke-width="1.5" d="M1 1l6 6m0-6L1 7"/>
</svg>
</button>
</p>
@endforeach
</div>
<?php
$aSelfPath = explode('/', SELF_PATH);
if(empty($_SERVER['PHP_AUTH_USER']) || empty($_SERVER['PHP_AUTH_PW']))
{
header('WWW-Authenticate: Basic realm="Feed protected"');
header('HTTP/1.0 401 Unauthorized');
echo 'Access denied.';
exit;
}
elseif(sizeof($aSelfPath) === 4)
{
$sFeedType = strtolower($aSelfPath[2]); // google or facebook
$iFeedId = intval($aSelfPath[3]);
if($oFeed = oFeed::getResourceById($iFeedId))
{
if(($_SERVER['PHP_AUTH_USER'] !== $oFeed->s('auth_user')) || (clsString::toHash($_SERVER['PHP_AUTH_PW']) !== $oFeed->s('auth_pass'))) // VAlidate password with hashed password in DB.
{
header('WWW-Authenticate: Basic realm="Feed protected"');
header('HTTP/1.0 401 Unauthorized');
echo 'Access denied.';
exit;
}
if($sFeedType == 'google')
{
$sFeedPath = ROOT_PATH . $oFeed->getTargetPath();
}
else
{
$sFeedPath = ROOT_PATH . $oFeed->getFacebookPath();
}
if(is_file($sFeedPath))
{
clsFile::output($sFeedPath);
}
}
}
header('HTTP/1.0 404 Not Found');
echo 'File not found.';
exit;
public static function import_CleanupDescription($sValue, $aParams = [])
{
if($sValue = trim($sValue))
{
$sValue = str_replace(TAB, SPACE, $sValue);
$sValue = str_replace(NBSP, ' ', $sValue);
if(strpos($sValue, '\n') !== false)
{
$sValue = str_replace([CRLF, CR, '\r\n', '\n', '\r'], CRLF, $sValue);
}
// Contains <HTML>?
if(clsSyntax::isHtml($sValue))
{
$sValue = clsString::removeHtml($sValue ?? '');
// Replace HTML parsing errors
$aSearch = ['&', '&', '&&'];
$aReplace = ['&', '&', '&'];
$sValue = str_replace($aSearch, $aReplace, $sValue);
// Remove common special symbols
$aSearch = ['"', ''', '°', '&lsquo', ' ', '&rsquo', '®', '²'];
$aReplace = ['"', '\'', DEGREE, '"', '"', ' ', '®', '2'];
$sValue = str_replace($aSearch, $aReplace, $sValue);
}
$sValue = preg_replace('/[\r][\n][ ]+/', CRLF, $sValue);
$sValue = preg_replace('/[ ]+[\r][\n]/', CRLF, $sValue);
if($iOffset = mb_strpos($sValue, 'Reviews:'))
{
$sValue = mb_substr($sValue, 0, $iOffset);
}
while($iOffset = mb_strpos($sValue, '[embed]'))
{
if($iOffset2 = mb_strpos($sValue, '[/embed]', $iOffset))
{
$sValue = mb_substr($sValue, 0, $iOffset) . mb_substr($sValue, $iOffset2 + 8);
}
}
$sValue = trim($sValue);
$sValue = str_replace(CRLF . CRLF, CRLF, $sValue);
$sValue = static::import_CleanupUppercaseLines($sValue, $aParams);
$aParams['length'] = ($aParams['length'] ?? 5000);
$sValue = static::import_CleanupLength($sValue, $aParams);
}
return $sValue;
}
$aResult = [];
$sLanguage = ((empty($aParams['language']) || !in_array($aParams['language'], ['de-de', 'fr-fr', 'nl-nl'])) ? 'en-us' : $aParams['language']);
if($sValue)
{
foreach($aColors as $aColor)
{
foreach($aColor as $v)
{
$sRegex = '/([a-z]*)(' . $v . ')([a-z]*)/i';
$aMatches = [];
if(preg_match_all($sRegex, $sValue, $aMatches))
{
foreach($aMatches[0] as $i => $s)
{
if(($aMatches[1][$i] == '') && in_array($aMatches[3][$i], ['', 'color', 'kleur', 'kleurig']))
{
$aResult[] = $aColor[$sLanguage];
break(2);
}
}
}
}
}
if($aResult)
{
$aResult = array_unique($aResult);
}
}
return implode('/', $aResult);
}
public static function parse_UnitDetect($sValue, $aParams = [])
{
$sRegex = '/(([0-9]+)([\.,][0-9]+)?)[ ]?([a-zA-Z]+)/';
$aMatches = [];
return '';
}
interface GoogleButtonProps {
onConnected: (googleAccessToken? : string, googleAuthorizationToken?: string, googleAuthorizationUrl?: string) => void;
onStart?: () => void;
onFailed: () => void;
onCancelled?: () => void;
scopes?: string[];
label: string;
style?: any;
flow?: 'implicit' | 'auth-code';
isLoading: boolean;
}
export default function GoogleConnectButton(props: GoogleButtonProps) {
const { colors } = useTheme();
var needRefreshToken = (props.flow || 'auth-code') == 'auth-code';
useEffect(() => {
GoogleSignin.configure({
webClientId: process.env.EXPO_PUBLIC_WEB_GOOGLE_CLIENT_ID, // client ID of type WEB for your server. Required to get the `idToken` on the user object, and for offline access.
scopes: props.scopes || [
"https://www.googleapis.com/auth/analytics.readonly",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile"
], // what API you want to access on behalf of the user, default is email and profile
forceCodeForRefreshToken: needRefreshToken, // [Android] related to `serverAuthCode`, read the docs link below *.
offlineAccess: needRefreshToken, // if you want to access Google API on behalf of the user FROM YOUR SERVER
});
}, []);
const signOut = async () => {
try {
await GoogleSignin.signOut();
} catch (ex : unknown) {
logAnalyticsEvent("GoogleTokenError", ex);
}
};
const _signIn = async () => {
try {
await GoogleSignin.hasPlayServices();
await signOut();
const userInfo = await GoogleSignin.signIn();
var accessToken = await GoogleSignin.getTokens();
await GoogleSignin.clearCachedAccessToken(accessToken.accessToken);
accessToken = await GoogleSignin.getTokens();
if (userInfo?.user?.email == null || (userInfo?.serverAuthCode == null && accessToken == null)) {
throw new ValidationError("Invalid Google Account");
}
await props.onConnected(
needRefreshToken ? undefined : accessToken.accessToken,
needRefreshToken ? userInfo?.serverAuthCode ?? undefined : undefined,
needRefreshToken ? "" : undefined);
} catch (ex : unknown) {
if (ex?.code) {
switch (ex.code) {
case statusCodes.SIGN_IN_CANCELLED:
props.onCancelled && props.onCancelled();
break;
default:
props.onFailed();
}
}else{
props.onFailed();
}
await logAnalyticsEvent("GoogleConnect_Error", ex);
}
};
return (
<Button
label={props.label || "Continue with Google"}
onPress={()=>{
props.onStart && props.onStart();
_signIn();
}}
containerStyle={[styles.button, { borderColor:colors.primaryBackground, ...props.style}]}
backgroundColor={colors.primaryText}
loadingColor={colors.primaryBackground}
disabled={props.isLoading}
loading={props.isLoading}
customComponent={
<View style={{flexDirection:"row"}}>
<GoogleLogo width={24} height={24} />
<Text style={{ paddingLeft: scale(10),alignSelf:"center", color: colors.primaryBackground, fontSize: scale(16),fontFamily: Fonts.OUTFIT_MEDIUM}}>
{props.label || "Continue with Google"}
</Text>
</View>
}
/>
);
}
const styles = StyleSheet.create({
button: {
width: WINDOW_WIDTH - scale(40),
height: scale(45),
justifyContent: "center",
alignItems: "center",
borderWidth: 1,
},
});export default function GoogleLoginButton(props:{
setLoading: (loading: boolean) => void ,
label?:string | undefined,
scopes?: string[],
flow?: 'implicit' | 'auth-code',
eventPrefix?: string}) {
const [isLoading, setIsLoading] = useState(false);
const signupUser = useAuthStore((state) => state.signupUser);
const analyticsMount = useAnalyticsStore((state) => state.onMount);
const setAuthenticated = useAuthStore((state) => state.setAuthenticated);
const signupUserFlow = async (googleAccessToken?: string, googleAuthorizationToken?: string, googleAuthorizationUrl?: string) => {
try {
var user = await signupUser({ accessToken: googleAccessToken, authorizationCode : googleAuthorizationToken, authorizationUrl: googleAuthorizationUrl});
logAnalyticsEvent((props.eventPrefix || "Login") + "_Success", {email: user?.email, "source": "google"});
await analyticsMount();
await setAuthenticated();
} catch (ex :unknown){
if (ex instanceof ValidationError){
Message("Error", "Error with acccount. Please try again later.");
}
logAnalyticsEvent((props.eventPrefix || "Login") + "_Error", ex);
}finally{
setIsLoading(false);
props.setLoading(false);
}
};
return (
<GoogleConnectButton
label={props.label || "Continue with Google"}
flow={props.flow || 'implicit'}
scopes={props.scopes || ["https://www.googleapis.com/auth/userinfo.email","https://www.googleapis.com/auth/userinfo.profile"]}
onStart= {() => {
setIsLoading(true);
props.setLoading(true);
}}
onConnected={ (googleAccessToken? : string, googleAuthorizationToken?: string, googleAuthorizationUrl?: string)=>{
signupUserFlow(googleAccessToken, googleAuthorizationToken, googleAuthorizationUrl);
}}
onCancelled={() => {
setIsLoading(false);
props.setLoading(false);
}}
onFailed={() => {
setIsLoading(false);
props.setLoading(false);
logAnalyticsEvent("GoogleButtonError");
Message("Error", "Error with Google. Please try again later.");
}}
isLoading={isLoading}
/>
)
}