From 6d44a5d0390addf5006a08cac45521ab108b9315 Mon Sep 17 00:00:00 2001 From: gkatrakazas Date: Wed, 15 Jan 2025 16:05:23 +0200 Subject: [PATCH] Fix lint warnings --- .../services/OpenID4VCI/CredentialRequest.tsx | 142 ++++++++++-------- src/lib/services/OpenID4VCI/OpenID4VCI.tsx | 26 ++-- src/lib/services/OpenID4VCI/TokenRequest.tsx | 131 +++++++--------- .../OpenID4VCIClientStateRepository.tsx | 28 ++-- 4 files changed, 164 insertions(+), 163 deletions(-) diff --git a/src/lib/services/OpenID4VCI/CredentialRequest.tsx b/src/lib/services/OpenID4VCI/CredentialRequest.tsx index 39d5066fd..aa002be84 100644 --- a/src/lib/services/OpenID4VCI/CredentialRequest.tsx +++ b/src/lib/services/OpenID4VCI/CredentialRequest.tsx @@ -2,64 +2,66 @@ import { JWK, KeyLike } from "jose"; import { generateDPoP } from "../../utils/dpop"; import { useHttpProxy } from "../HttpProxy/HttpProxy"; import { useOpenID4VCIHelper } from "../OpenID4VCIHelper"; -import { useContext } from "react"; +import { useContext, useCallback, useMemo, useRef } from "react"; import SessionContext from "../../../context/SessionContext"; import { VerifiableCredentialFormat } from "../../schemas/vc"; - export function useCredentialRequest() { const httpProxy = useHttpProxy(); const openID4VCIHelper = useOpenID4VCIHelper(); const { keystore, api } = useContext(SessionContext); - let credentialEndpointURL = null; - let access_token = null; - let c_nonce = null; - let dpop_nonce = null; - - let dpopPrivateKey: KeyLike; - let dpopPublicKeyJwk: JWK; - let jti: string; - - let credentialIssuerIdentifier: string; + const credentialEndpointURLRef = useRef(null); + const accessTokenRef = useRef(null); + const cNonceRef = useRef(null); + const dpopNonceRef = useRef(null); + const dpopPrivateKeyRef = useRef(null); + const dpopPublicKeyJwkRef = useRef(null); + const jtiRef = useRef(null); + const credentialIssuerIdentifierRef = useRef(null); - const httpHeaders = { + const httpHeaders = useMemo(() => ({ 'Content-Type': 'application/json', - }; - - function setCredentialEndpoint(endpoint: string) { - credentialEndpointURL = endpoint; - } - - function setCNonce(cNonce: string) { - c_nonce = cNonce; - } - - function setAccessToken(at: string) { - access_token = at; - httpHeaders['Authorization'] = `Bearer ${access_token}`; - } - - function setDpopNonce(dpopNonce: string) { - dpop_nonce = dpopNonce; - } - - function setDpopPrivateKey(sk: KeyLike) { - dpopPrivateKey = sk; - } - - function setDpopPublicKeyJwk(jwk: JWK) { - dpopPublicKeyJwk = jwk; - } - - function setDpopJti(id: string) { - jti = id; - } - - - async function setDpopHeader() { - if (!credentialEndpointURL) { - throw new Error("CredentialRequest: credentialEndpointURL was not defined"); + }), []); + + const setCredentialEndpoint = useCallback((endpoint: string) => { + credentialEndpointURLRef.current = endpoint; + }, []); + + const setCNonce = useCallback((cNonce: string) => { + cNonceRef.current = cNonce; + }, []); + + const setAccessToken = useCallback((at: string) => { + accessTokenRef.current = at; + httpHeaders['Authorization'] = `Bearer ${at}`; + }, [httpHeaders]); + + const setDpopNonce = useCallback((dpopNonce: string) => { + dpopNonceRef.current = dpopNonce; + }, []); + + const setDpopPrivateKey = useCallback((sk: KeyLike) => { + dpopPrivateKeyRef.current = sk; + }, []); + + const setDpopPublicKeyJwk = useCallback((jwk: JWK) => { + dpopPublicKeyJwkRef.current = jwk; + }, []); + + const setDpopJti = useCallback((id: string) => { + jtiRef.current = id; + }, []); + + const setDpopHeader = useCallback(async () => { + const credentialEndpointURL = credentialEndpointURLRef.current; + const dpopPublicKeyJwk = dpopPublicKeyJwkRef.current; + const jti = jtiRef.current; + const dpopNonce = dpopNonceRef.current; + const accessToken = accessTokenRef.current; + + if (!credentialEndpointURL || !dpopPublicKeyJwk || !jti) { + throw new Error("Missing required parameters for DPoP header"); } if (!dpopPublicKeyJwk) { @@ -71,27 +73,29 @@ export function useCredentialRequest() { } const credentialEndpointDPoP = await generateDPoP( - dpopPrivateKey, + dpopPrivateKeyRef.current, dpopPublicKeyJwk, jti, "POST", credentialEndpointURL, - dpop_nonce, - access_token + dpopNonce, + accessToken ); - httpHeaders['Authorization'] = `DPoP ${access_token}`; + httpHeaders['Authorization'] = `DPoP ${accessToken}`; httpHeaders['dpop'] = credentialEndpointDPoP; - } + }, [httpHeaders]); - function setCredentialIssuerIdentifier(id: string) { - credentialIssuerIdentifier = id; - } + const setCredentialIssuerIdentifier = useCallback((id: string) => { + credentialIssuerIdentifierRef.current = id; + }, []); - async function execute(credentialConfigurationId: string, cachedProofs?: any): Promise<{ credentialResponse: any }> { + const execute = useCallback(async (credentialConfigurationId: string, cachedProofs?: any): Promise<{ credentialResponse: any }> => { console.log("Executing credential request..."); - const [authzServerMetadata, credentialIssuerMetadata, clientId] = await Promise.all([ - openID4VCIHelper.getAuthorizationServerMetadata(credentialIssuerIdentifier), + const credentialIssuerIdentifier = credentialIssuerIdentifierRef.current; + const c_nonce = cNonceRef.current; + + const [credentialIssuerMetadata, clientId] = await Promise.all([ openID4VCIHelper.getCredentialIssuerMetadata(credentialIssuerIdentifier), openID4VCIHelper.getClientId(credentialIssuerIdentifier) ]); @@ -150,7 +154,7 @@ export function useCredentialRequest() { console.log("Credential endpoint body = ", credentialEndpointBody); - const credentialResponse = await httpProxy.post(credentialEndpointURL, credentialEndpointBody, httpHeaders); + const credentialResponse = await httpProxy.post(credentialEndpointURLRef.current, credentialEndpointBody, httpHeaders); if (credentialResponse.err) { console.log("Error: Credential response = ", JSON.stringify(credentialResponse.err)); @@ -165,9 +169,20 @@ export function useCredentialRequest() { throw new Error("Credential Request failed"); } return { credentialResponse }; - } + }, [api, httpProxy, keystore, openID4VCIHelper, setDpopHeader, setDpopNonce, httpHeaders]); - return { + return useMemo(() => ({ + setCredentialEndpoint, + setCNonce, + setAccessToken, + setDpopNonce, + setDpopPrivateKey, + setDpopPublicKeyJwk, + setDpopJti, + setDpopHeader, + setCredentialIssuerIdentifier, + execute, + }), [ setCredentialEndpoint, setCNonce, setAccessToken, @@ -177,7 +192,6 @@ export function useCredentialRequest() { setDpopJti, setDpopHeader, setCredentialIssuerIdentifier, - execute, - } + ]); } diff --git a/src/lib/services/OpenID4VCI/OpenID4VCI.tsx b/src/lib/services/OpenID4VCI/OpenID4VCI.tsx index a799a64a6..2d408dfff 100644 --- a/src/lib/services/OpenID4VCI/OpenID4VCI.tsx +++ b/src/lib/services/OpenID4VCI/OpenID4VCI.tsx @@ -8,7 +8,7 @@ import { generateRandomIdentifier } from '../../utils/generateRandomIdentifier'; import * as config from '../../../config'; import { useHttpProxy } from '../HttpProxy/HttpProxy'; import { useOpenID4VCIClientStateRepository } from '../OpenID4VCIClientStateRepository'; -import { useCallback, useContext, useMemo } from 'react'; +import { useCallback, useContext, useMemo, useEffect, useRef } from 'react'; import SessionContext from '../../../context/SessionContext'; import { useOpenID4VCIPushedAuthorizationRequest } from './OpenID4VCIAuthorizationRequest/OpenID4VCIPushedAuthorizationRequest'; import { useOpenID4VCIAuthorizationRequestForFirstPartyApplications } from './OpenID4VCIAuthorizationRequest/OpenID4VCIAuthorizationRequestForFirstPartyApplications'; @@ -23,7 +23,7 @@ export function useOpenID4VCI({ errorCallback }: { errorCallback: (title: string const httpProxy = useHttpProxy(); const openID4VCIClientStateRepository = useOpenID4VCIClientStateRepository(); - const { keystore, api } = useContext(SessionContext); + const { api } = useContext(SessionContext); const openID4VCIHelper = useOpenID4VCIHelper(); @@ -40,10 +40,8 @@ export function useOpenID4VCI({ errorCallback }: { errorCallback: (title: string data: { access_token, c_nonce }, } = response; - const [authzServerMetadata, credentialIssuerMetadata, clientId] = await Promise.all([ - openID4VCIHelper.getAuthorizationServerMetadata(flowState.credentialIssuerIdentifier), - openID4VCIHelper.getCredentialIssuerMetadata(flowState.credentialIssuerIdentifier), - openID4VCIHelper.getClientId(flowState.credentialIssuerIdentifier) + const [credentialIssuerMetadata] = await Promise.all([ + openID4VCIHelper.getCredentialIssuerMetadata(flowState.credentialIssuerIdentifier) ]); credentialRequestBuilder.setCredentialEndpoint(credentialIssuerMetadata.metadata.credential_endpoint); @@ -99,7 +97,7 @@ export function useOpenID4VCI({ errorCallback }: { errorCallback: (title: string return; }, - [openID4VCIHelper, api, httpProxy, keystore, openID4VCIClientStateRepository] + [openID4VCIHelper, api, openID4VCIClientStateRepository, credentialRequestBuilder] ); const requestCredentials = useCallback( @@ -230,8 +228,8 @@ export function useOpenID4VCI({ errorCallback }: { errorCallback: (title: string const result = await tokenRequestBuilder.execute(); if ('error' in result) { - if (result.error == TokenRequestError.AUTHORIZATION_REQUIRED) { - return generateAuthorizationRequest(flowState.credentialIssuerIdentifier, flowState.credentialConfigurationId); + if (result.error === TokenRequestError.AUTHORIZATION_REQUIRED) { + return generateAuthorizationRequestRef.current(flowState.credentialIssuerIdentifier, flowState.credentialConfigurationId); } throw new Error("Token request failed"); } @@ -270,11 +268,13 @@ export function useOpenID4VCI({ errorCallback }: { errorCallback: (title: string [ openID4VCIClientStateRepository, openID4VCIHelper, - httpProxy, credentialRequest, + tokenRequestBuilder, ] ); + const generateAuthorizationRequestRef = useRef(null); + const handleAuthorizationResponse = useCallback( async (url: string, dpopNonceHeader?: string) => { console.log('handleAuthorizationResponse'); @@ -435,6 +435,12 @@ export function useOpenID4VCI({ errorCallback }: { errorCallback: (title: string [openID4VCIClientStateRepository, openID4VCIHelper, handleAuthorizationResponse, openID4VCIAuthorizationRequestForFirstPartyApplications, openID4VCIPushedAuthorizationRequest, requestCredentials] ); + // Step 3: Update `useRef` with the `generateAuthorizationRequest` function + useEffect(() => { + console.log('call and call') + generateAuthorizationRequestRef.current = generateAuthorizationRequest; + }, [generateAuthorizationRequest]); + return useMemo(() => { return { generateAuthorizationRequest, diff --git a/src/lib/services/OpenID4VCI/TokenRequest.tsx b/src/lib/services/OpenID4VCI/TokenRequest.tsx index 5341bdef2..45d6515c2 100644 --- a/src/lib/services/OpenID4VCI/TokenRequest.tsx +++ b/src/lib/services/OpenID4VCI/TokenRequest.tsx @@ -1,6 +1,7 @@ import { JWK, KeyLike } from 'jose'; import { useHttpProxy } from '../HttpProxy/HttpProxy'; import { generateDPoP } from '../../utils/dpop'; +import { useMemo, useCallback, useState } from 'react'; export type AccessToken = { access_token: string; @@ -29,82 +30,58 @@ export function useTokenRequest() { const httpProxy = useHttpProxy(); - let tokenEndpointURL = null; + const [tokenEndpointURL, setTokenEndpoint] = useState(null); + const [grantType, setGrantType] = useState(GrantType.AUTHORIZATION_CODE); + const [refresh_token, setRefreshToken] = useState(null); + const [code, setAuthorizationCode] = useState(null); + const [codeVerifier, setCodeVerifier] = useState(null); + const [redirect_uri, setRedirectUri] = useState(null); + const [clientId, setClientId] = useState(null); - let grant_type: GrantType = GrantType.AUTHORIZATION_CODE; - let refresh_token = null; - let code = null; - let code_verifier = null; - let redirect_uri = null; - let client_id = null; - const httpHeaders = { + const httpHeaders = useMemo(() => ({ 'Content-Type': 'application/x-www-form-urlencoded', - }; + }), []); - function setClientId(clientId: string) { - client_id = clientId; - } - - function setGrantType(grant: GrantType) { - grant_type = grant; - } - - function setAuthorizationCode(authzCode: string) { - code = authzCode; - } - - function setCodeVerifier(codeVerifier: string) { - code_verifier = codeVerifier; - } - - function setRefreshToken(tok: string) { - refresh_token = tok; - } - - function setRedirectUri(redirectUri: string) { - redirect_uri = redirectUri; - } - - function setTokenEndpoint(tokenEndpoint: string) { - tokenEndpointURL = tokenEndpoint; - } - - async function setDpopHeader(dpopPrivateKey: KeyLike, dpopPublicKeyJwk: JWK, jti: string) { - if (!tokenEndpointURL) { - throw new Error("tokenEndpointURL was not defined"); - } - const dpop = await generateDPoP( - dpopPrivateKey as KeyLike, - dpopPublicKeyJwk, - jti, - "POST", - tokenEndpointURL, - httpHeaders['dpop-nonce'] - ); - - httpHeaders['DPoP'] = dpop; - } - - - async function execute(): Promise<{ response: AccessToken } | { error: TokenRequestError, response?: any }> { + const setDpopHeader = useCallback( + async (dpopPrivateKey: KeyLike, dpopPublicKeyJwk: JWK, jti: string) => { + if (!tokenEndpointURL) { + throw new Error("tokenEndpointURL was not defined"); + } + const dpop = await generateDPoP( + dpopPrivateKey as KeyLike, + dpopPublicKeyJwk, + jti, + "POST", + tokenEndpointURL, + httpHeaders['dpop-nonce'] + ); + + httpHeaders['DPoP'] = dpop; + }, + [tokenEndpointURL, httpHeaders] + ); + + const execute = useCallback(async (): Promise< + { response: AccessToken } | { error: TokenRequestError; response?: any } + > => { const formData = new URLSearchParams(); - formData.append('client_id', client_id); - if (grant_type === GrantType.AUTHORIZATION_CODE) { + formData.append('clientId', clientId); + if (grantType === GrantType.AUTHORIZATION_CODE) { console.log("Executing authorization code grant..."); - formData.append('grant_type', 'authorization_code'); + formData.append('grantType', 'authorization_code'); formData.append('code', code); - formData.append('code_verifier', code_verifier); + formData.append('codeVerifier', codeVerifier); } - else if (grant_type === GrantType.REFRESH) { + else if (grantType === GrantType.REFRESH) { console.log("Executing refresh token grant..."); if (!refresh_token) { console.info("Found no refresh_token to execute refesh_token grant") throw new Error("Found no refresh_token to execute refesh_token grant"); } - formData.append('grant_type', 'refresh_token'); + formData.append('grantType', 'refresh_token'); formData.append('refresh_token', refresh_token); } else { @@ -135,7 +112,7 @@ export function useTokenRequest() { } return { - response: { + response: { access_token: response.data.access_token, c_nonce: response.data.c_nonce, c_nonce_expires_in: response.data.c_nonce_expires_in, @@ -146,18 +123,20 @@ export function useTokenRequest() { } } } - } - - return { - setClientId, - setGrantType, - setAuthorizationCode, - setCodeVerifier, - setRefreshToken, - setRedirectUri, - setTokenEndpoint, - setDpopHeader, - - execute, - } + }, [clientId, code, codeVerifier, grantType, httpHeaders, httpProxy, redirect_uri, refresh_token, tokenEndpointURL]); + + return useMemo( + () => ({ + setClientId, + setGrantType, + setAuthorizationCode, + setCodeVerifier, + setRefreshToken, + setRedirectUri, + setTokenEndpoint, + setDpopHeader, + execute, + }), + [setClientId, setGrantType, setAuthorizationCode, setCodeVerifier, setRefreshToken, setRedirectUri, setTokenEndpoint, setDpopHeader, execute] + ); } diff --git a/src/lib/services/OpenID4VCIClientStateRepository.tsx b/src/lib/services/OpenID4VCIClientStateRepository.tsx index c028c9ca9..ae96ba547 100644 --- a/src/lib/services/OpenID4VCIClientStateRepository.tsx +++ b/src/lib/services/OpenID4VCIClientStateRepository.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect, useState, useCallback, useMemo } from "react"; +import { useContext, useCallback, useMemo } from "react"; import { IOpenID4VCIClientStateRepository } from "../interfaces/IOpenID4VCIClientStateRepository"; import { OpenID4VCIClientState } from "../types/OpenID4VCIClientState"; import SessionContext from "../../context/SessionContext"; @@ -13,7 +13,7 @@ export function useOpenID4VCIClientStateRepository(): IOpenID4VCIClientStateRepo localStorage.setItem(key, JSON.stringify([])); } - const getRememberIssuerAge = async (): Promise => { + const getRememberIssuerAge = useCallback(async (): Promise => { if (!api || !isLoggedIn) { return null; } @@ -21,17 +21,17 @@ export function useOpenID4VCIClientStateRepository(): IOpenID4VCIClientStateRepo const userData = response.data; return userData.settings.openidRefreshTokenMaxAgeInSeconds as number; }); - } + }, [api, isLoggedIn]); const getByCredentialIssuerIdentifierAndCredentialConfigurationIdAndUserHandle = useCallback(async ( - credentialIssuer: string, - credentialConfigurationId: string - ): Promise => { + credentialIssuer: string, + credentialConfigurationId: string + ): Promise => { - const array = JSON.parse(localStorage.getItem(key)) as Array; - const res = array.filter((s) => s.credentialIssuerIdentifier === credentialIssuer && s.credentialConfigurationId === credentialConfigurationId && s.userHandleB64U === keystore.getUserHandleB64u())[0]; - return res ? res : null; - }, + const array = JSON.parse(localStorage.getItem(key)) as Array; + const res = array.filter((s) => s.credentialIssuerIdentifier === credentialIssuer && s.credentialConfigurationId === credentialConfigurationId && s.userHandleB64U === keystore.getUserHandleB64u())[0]; + return res ? res : null; + }, [keystore] ); @@ -64,7 +64,7 @@ export function useOpenID4VCIClientStateRepository(): IOpenID4VCIClientStateRepo console.log("Cleanup states = ", statesToBeRemoved) const filteredArray = array.filter((s) => !statesToBeRemoved.includes(s.state)); localStorage.setItem(key, JSON.stringify(filteredArray)); - }, [keystore]); + }, [keystore, getRememberIssuerAge]); const create = useCallback( async (state: OpenID4VCIClientState): Promise => { @@ -110,13 +110,15 @@ export function useOpenID4VCIClientStateRepository(): IOpenID4VCIClientStateRepo getByStateAndUserHandle, cleanupExpired, create, - updateState + updateState, + getRememberIssuerAge, } }, [ getByCredentialIssuerIdentifierAndCredentialConfigurationIdAndUserHandle, getByStateAndUserHandle, cleanupExpired, create, - updateState + updateState, + getRememberIssuerAge, ]); }