Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 100 display token in api UI #127

Merged
merged 2 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 114 additions & 24 deletions web-app/src/app/screens/Account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,58 @@
Snackbar,
} from '@mui/material';
import { Visibility, VisibilityOff, ContentCopy } from '@mui/icons-material';

import { useSelector } from 'react-redux';
import { selectUserProfile } from '../store/selectors';
interface APIAccountState {
apiKey: string;
showApiKey: boolean;
refreshToken: string;
showRefreshToken: boolean;
accessToken: string;
showAccessToken: boolean;
}

export default function APIAccount(): React.ReactElement {
const [values, setValues] = React.useState<APIAccountState>({
apiKey: 'Your key is hidden',
showApiKey: false,
refreshToken: 'Your refresh token is hidden',
showRefreshToken: false,
accessToken: 'Your access token is hidden',
showAccessToken: false,
});
const user = useSelector(selectUserProfile);
const [openSnackbar, setOpenSnackbar] = React.useState(false);

const handleClickShowApiKey = (): void => {
setValues({
...values,
showApiKey: !values.showApiKey,
apiKey: values.showApiKey
? 'Your key is hidden'
: 'your-api-key-here-12345',
});
const handleClickShowApiKey = (tokenType: 'access' | 'refresh'): void => {
switch (tokenType) {
case 'access':
setValues({
...values,
showAccessToken: !values.showAccessToken,
accessToken: values.showAccessToken
? 'Your access token is hidden'
: user?.accessToken ?? 'Your access token is unavailable',
});
break;
case 'refresh':
setValues({
...values,
showRefreshToken: !values.showRefreshToken,
refreshToken: values.showRefreshToken
? 'Your refresh token is hidden'
: user?.refreshToken ?? 'Your refresh token is unavailable',
});
break;
default:
break;
}
};

const handleCopyToClipboard = (): void => {
const handleCopyToClipboard = (token: string): void => {
navigator.clipboard
.writeText('your-api-key-here-12345')
.writeText(token)
.then(() => {
setOpenSnackbar(true);
})
.catch((error) => {
console.error('Could not copy text: ', error);

Check warning on line 62 in web-app/src/app/screens/Account.tsx

View workflow job for this annotation

GitHub Actions / Test

Unexpected console statement
});
};

Expand Down Expand Up @@ -77,36 +98,105 @@
color='primary'
sx={{ mt: 2, fontWeight: 'bold' }}
>
API Key
Refresh Token
</Typography>
<Typography
width={250}
variant='body1'
sx={{
display: 'inline-block',
mr: 2,
background: '#e6e6e6',
padding: 1,
borderRadius: 2,
wordBreak: 'break-word',
}}
>
{values.apiKey}
{values.refreshToken}
</Typography>
<IconButton
aria-label='Copy API key to clipboard'
aria-label='Copy Refresh Token to clipboard'
edge='end'
onClick={handleCopyToClipboard}
onClick={() => {
handleCopyToClipboard(values.refreshToken);
}}
sx={{ display: 'inline-block', verticalAlign: 'middle' }}
>
<ContentCopy />
</IconButton>
<IconButton
aria-label='toggle API key visibility'
onClick={handleClickShowApiKey}
aria-label='toggle Refresh Token visibility'
onClick={() => {
handleClickShowApiKey('refresh');
}}
edge='end'
sx={{ display: 'inline-block', verticalAlign: 'middle' }}
>
{values.showRefreshToken ? <VisibilityOff /> : <Visibility />}
</IconButton>

<Snackbar
open={openSnackbar}
autoHideDuration={2000}
onClose={() => {
setOpenSnackbar(false);
}}
message='Your Refresh Token is copied to your clipboard.'
anchorOrigin={{ horizontal: 'center', vertical: 'bottom' }}
action={
<React.Fragment>
<Button
color='primary'
size='small'
onClick={() => {
setOpenSnackbar(false);
}}
>
OK
</Button>
</React.Fragment>
}
/>

<Typography
component='h2'
variant='h6'
color='primary'
sx={{ mt: 2, fontWeight: 'bold' }}
>
Access Token
</Typography>
<Typography
variant='body1'
sx={{
display: 'inline-block',
mr: 2,
background: '#e6e6e6',
padding: 1,
borderRadius: 2,
wordBreak: 'break-word',
}}
>
{values.accessToken}
</Typography>
<IconButton
aria-label='Copy Access Token to clipboard'
edge='end'
onClick={() => {
handleCopyToClipboard(values.accessToken);
}}
sx={{ display: 'inline-block', verticalAlign: 'middle' }}
>
<ContentCopy />
</IconButton>
<IconButton
aria-label='toggle Access Token visibility'
onClick={() => {
handleClickShowApiKey('access');
}}
edge='end'
sx={{ display: 'inline-block', verticalAlign: 'middle' }}
>
{values.showApiKey ? <VisibilityOff /> : <Visibility />}
{values.showAccessToken ? <VisibilityOff /> : <Visibility />}
</IconButton>

<Snackbar
Expand All @@ -115,7 +205,7 @@
onClose={() => {
setOpenSnackbar(false);
}}
message='Your API key is copied to your clipboard.'
message='Your Access Token is copied to your clipboard.'
anchorOrigin={{ horizontal: 'center', vertical: 'bottom' }}
action={
<React.Fragment>
Expand Down
8 changes: 7 additions & 1 deletion web-app/src/app/services/profile-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,22 @@ export const sendEmailVerification = async (): Promise<void> => {
/**
* Return the current user or null if the user is not logged in.
*/
export const getUserFromSession = (): User | null => {
export const getUserFromSession = async (): Promise<User | null> => {
const currentUser = app.auth().currentUser;
if (currentUser === null) {
return null;
}
const idTokenResult = await currentUser.getIdTokenResult(true);
const refreshToken = currentUser.refreshToken;
const accessToken = idTokenResult.token;
// const expiresIn = idTokenResult.expirationTime;
return {
fullname: currentUser?.displayName ?? undefined,
email: currentUser?.email ?? '',
// Organization cannot be retrieved from the current user
organization: undefined,
refreshToken,
accessToken,
};
};

Expand Down
4 changes: 2 additions & 2 deletions web-app/src/app/store/saga/auth-saga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ function* signUpSaga({
}>): Generator {
try {
yield app.auth().createUserWithEmailAndPassword(email, password);
const user = getUserFromSession();
const user = yield call(getUserFromSession);
if (user === null) {
throw new Error('User not found');
}
yield put(signUpSuccess(user));
yield put(signUpSuccess(user as User));
navigateTo(redirectScreen);
} catch (error) {
yield put(signUpFail(getAppError(error)));
Expand Down
2 changes: 2 additions & 0 deletions web-app/src/app/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export interface User {
fullname?: string;
email: string;
organization?: string;
accessToken?: string;
refreshToken?: string;
}

export const USER_PROFILE = 'userProfile';
Expand Down
Loading