dosubot[bot] commented on issue #7821:
URL: 
https://github.com/apache/incubator-devlake/issues/7821#issuecomment-2263225635

   <!-- Greeting -->
   @yurii-chikh-vitech Hello there! I'm here to help you with any bugs, 
questions, or contributions you may have. Let's work together to resolve the 
issue you've encountered.
   
   <!-- Answer -->
   To prevent the secure Personal Access Token (PAT) from being exposed in the 
network tab for the request to the URL 
`http://localhost:4000/api/plugins/gitlab/connections/1/scopes?page=1&pageSize=10&blueprints=true`
 in the DevLake configuration UI, you should ensure that the token is not sent 
directly in the request URL or headers in a way that it can be easily 
intercepted.
   
   One common approach is to use a backend service to handle the token 
securely. The frontend can make a request to the backend, which then adds the 
token and forwards the request to the GitLab API. This way, the token is never 
exposed to the client-side code or network tab.
   
   Here is a simplified example of how you might implement this:
   
   1. **Backend Service**: Create an endpoint in your backend service that 
handles the request to GitLab.
   
   ```javascript
   // backend-service.js
   const express = require('express');
   const axios = require('axios');
   const app = express();
   
   app.get('/api/gitlab/scopes', async (req, res) => {
     const { page, pageSize, blueprints } = req.query;
     const token = process.env.GITLAB_PAT; // Securely stored token
   
     try {
       const response = await axios.get(`https://gitlab.com/api/v4/projects`, {
         params: { page, pageSize, blueprints },
         headers: {
           'Authorization': `Bearer ${token}`
         }
       });
       res.json(response.data);
     } catch (error) {
       res.status(500).json({ error: error.message });
     }
   });
   
   app.listen(4000, () => {
     console.log('Backend service running on port 4000');
   });
   ```
   
   2. **Frontend Request**: Modify the frontend to call your backend service 
instead of directly calling the GitLab API.
   
   ```tsx
   // config.tsx
   import axios from 'axios';
   
   const fetchGitLabScopes = async (page, pageSize, blueprints) => {
     try {
       const response = await 
axios.get(`http://localhost:4000/api/gitlab/scopes`, {
         params: { page, pageSize, blueprints }
       });
       return response.data;
     } catch (error) {
       console.error('Error fetching GitLab scopes:', error);
       throw error;
     }
   };
   
   // Usage example
   fetchGitLabScopes(1, 10, true).then(data => {
     console.log('GitLab Scopes:', data);
   });
   ```
   
   Additionally, in the DevLake configuration UI, you can use the 
`ConnectionToken` component to handle the token input securely. Here is an 
example from the `Step2` component:
   
   ```tsx
   import { useState, useContext, useEffect, useMemo } from 'react';
   import { Flex, Button, Tooltip } from 'antd';
   import API from '@/api';
   import { Markdown } from '@/components';
   import { getPluginConfig } from '@/plugins';
   import { ConnectionToken } from 
'@/plugins/components/connection-form/fields/token';
   import { operator } from '@/utils';
   import { Context } from './context';
   import * as S from './styled';
   
   const paramsMap: Record<string, any> = {
     github: {
       authMethod: 'AccessToken',
       endpoint: 'https://api.github.com/',
     },
     gitlab: {
       endpoint: 'https://gitlab.com/api/v4/',
     },
     bitbucket: {
       endpoint: 'https://api.bitbucket.org/2.0/',
     },
     azuredevops: {},
   };
   
   export const Step2 = () => {
     const [QA, setQA] = useState('');
     const [operating, setOperating] = useState(false);
     const [testing, setTesting] = useState(false);
     const [testStaus, setTestStatus] = useState(false);
     const [payload, setPayload] = useState<any>({});
     const { step, records, done, projectName, plugin, setStep, setRecords } = 
useContext(Context);
   
     const config = useMemo(() => getPluginConfig(plugin as string), [plugin]);
   
     useEffect(() => {
       fetch(`/onboard/step-2/${plugin}.md`)
         .then((res) => res.text())
         .then((text) => setQA(text));
     }, [plugin]);
   
     const handleTest = async () => {
       if (!plugin) {
         return;
       }
   
       const [success] = await operator(
         async () =>
           await API.connection.testOld(plugin, {
             ...paramsMap[plugin],
             ...payload,
           }),
         {
           setOperating: setTesting,
           formatMessage: () => 'Connection success.',
           formatReason: () => 'Connection failed. Please check your token or 
network.',
         },
       );
   
       if (success) {
         setTestStatus(true);
       }
     };
   
     const handleSubmit = async () => {
       if (!plugin) {
         return;
       }
   
       const [success] = await operator(
         async () => {
           const connection = await API.connection.create(plugin, {
             name: `${plugin}-${Date.now()}`,
             ...paramsMap[plugin],
             ...payload,
           });
   
           const newRecords = [
             ...records,
             { plugin, connectionId: connection.id, blueprintId: '', 
pipelineId: '', scopeName: '' },
           ];
   
           setRecords(newRecords);
   
           await API.store.set('onboard', {
             step: 3,
             records: newRecords,
             done,
             projectName,
             plugin,
           });
         },
         {
           setOperating,
           hideToast: true,
         },
       );
   
       if (success) {
         setStep(step + 1);
       }
     };
   
     if (!plugin) {
       return null;
     }
   
     return (
       <>
         <S.StepContent>
           {['github', 'gitlab', 'azuredevops'].includes(plugin) && (
             <div className="content">
               <ConnectionToken
                 type="create"
                 label="Personal Access Token"
                 subLabel={`Create a personal access token in ${config.name}`}
                 initialValue=""
                 value={payload.token}
                 setValue={(token) => {
                   setPayload({ ...payload, token });
                   setTestStatus(false);
                 }}
                 error=""
                 setError={() => {}}
               />
               <Tooltip title="Test Connection">
                 <Button
                   style={{ marginTop: 16 }}
                   type="primary"
                   disabled={!payload.token}
                   loading={testing}
                   onClick={handleTest}
                 >
                   Connect
                 </Button>
               </Tooltip>
             </div>
           )}
           <Markdown className="qa">{QA}</Markdown>
         </S.StepContent>
         <Flex style={{ marginTop: 36 }} justify="space-between">
           <Button ghost type="primary" loading={operating} onClick={() => 
setStep(step - 1)}>
             Previous Step
           </Button>
           <Button type="primary" loading={operating} disabled={!testStaus} 
onClick={handleSubmit}>
             Next Step
           </Button>
         </Flex>
       </>
     );
   };
   ```
   
   By using this approach, the Personal Access Token (PAT) is kept secure on 
the server side and is not exposed in the network tab of the browser's 
developer tools 
<sup>[[1]](https://github.com/apache/incubator-devlake/blob/main/config-ui/src/plugins/register/gitlab/config.tsx)</sup><sup>[[2]](https://github.com/apache/incubator-devlake/blob/main/config-ui/src/routes/onboard/step-2.tsx)</sup>.
   
   
   <!-- Dosu Comment Footer -->
   *To continue talking to [Dosu](https://dosu.dev), mention @dosu.*
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to