Skip to content

Commit

Permalink
Merge pull request #164 from neelvirdy/nvirdy/session-vs-service-tokens
Browse files Browse the repository at this point in the history
Prevent mixing up session and service tokens, fix admin login, remove authenticate via API key
  • Loading branch information
neelvirdy committed Feb 2, 2023
2 parents 094adca + 58c63dd commit e93b6a3
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 105 deletions.
90 changes: 46 additions & 44 deletions pages/api-admin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,50 +170,52 @@ function APIPage(props: any) {
</th>
</tr>
{state.keys && state.keys.length
? state.keys.map((k, index) => {
const expiryDate = new Date(k.expiry);
const currentDate = new Date();
const isExpired = expiryDate < currentDate;

return (
<tr key={k.tokenHash ? k.tokenHash : k.token} className={tstyles.tr}>
<td style={{ opacity: isExpired ? 0.2 : 1 }} className={tstyles.td}>
{k.label}
</td>
<td style={{ opacity: isExpired ? 0.2 : 1 }} className={tstyles.td}>
{k.token ? k.token : REDACTED_TOKEN_STRING} {viewerToken === k.token ? <strong>(current browser session)</strong> : null}
</td>
<td style={{ opacity: isExpired ? 0.2 : 1 }} className={tstyles.td}>
{U.toDate(k.expiry)}
</td>
<td className={tstyles.td}>
<button
onClick={async () => {
const confirm = window.confirm('Are you sure you want to delete this key?');
if (!confirm) {
return;
}

const response = await R.del(`/user/api-keys/${k.tokenHash ? k.tokenHash : k.token}`, props.api);
if (viewerToken === k.token) {
window.location.href = '/';
return;
}

const keys = await R.get('/user/api-keys', props.api);
if (keys && !keys.error) {
setState({ ...state, keys });
}
}}
className={tstyles.tdbutton}
>
{isExpired ? 'Delete expired' : `Revoke`}
</button>
{!isExpired && k.token ? <CopyButton content={k.token} /> : null}
</td>
</tr>
);
})
? state.keys
.filter((k) => !k.isSession)
.map((k, index) => {
const expiryDate = new Date(k.expiry);
const currentDate = new Date();
const isExpired = expiryDate < currentDate;

return (
<tr key={k.tokenHash ? k.tokenHash : k.token} className={tstyles.tr}>
<td style={{ opacity: isExpired ? 0.2 : 1 }} className={tstyles.td}>
{k.label}
</td>
<td style={{ opacity: isExpired ? 0.2 : 1 }} className={tstyles.td}>
{k.token ? k.token : REDACTED_TOKEN_STRING} {viewerToken === k.token ? <strong>(current browser session)</strong> : null}
</td>
<td style={{ opacity: isExpired ? 0.2 : 1 }} className={tstyles.td}>
{U.toDate(k.expiry)}
</td>
<td className={tstyles.td}>
<button
onClick={async () => {
const confirm = window.confirm('Are you sure you want to delete this key?');
if (!confirm) {
return;
}

const response = await R.del(`/user/api-keys/${k.tokenHash ? k.tokenHash : k.token}`, props.api);
if (viewerToken === k.token) {
window.location.href = '/';
return;
}

const keys = await R.get('/user/api-keys', props.api);
if (keys && !keys.error) {
setState({ ...state, keys });
}
}}
className={tstyles.tdbutton}
>
{isExpired ? 'Delete expired' : `Revoke`}
</button>
{!isExpired && k.token ? <CopyButton content={k.token} /> : null}
</td>
</tr>
);
})
: null}
</tbody>
</table>
Expand Down
83 changes: 22 additions & 61 deletions pages/sign-in.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import Page from '@components/Page';
import SingleColumnLayout from '@components/SingleColumnLayout';
import Cookies from 'js-cookie';

import { H2, H3, H4, P } from '@components/Typography';
import { H2, H4, P } from '@components/Typography';

const ENABLE_SIGN_IN_WITH_FISSION = false;

Expand All @@ -36,24 +36,6 @@ export async function getServerSideProps(context) {
};
}

async function handleTokenAuthenticate(state: any, host) {
let response = await fetch(`${host}/user/stats`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${state.key}`,
},
});
if (response && response.status === 403) {
alert('Invalid API key');
} else if (response && response.status === 401) {
alert('Expired API key');
} else {
Cookies.set(C.auth, state.key);
window.location.reload();
}
return response;
}
async function handleSignIn(state: any, host) {
if (U.isEmpty(state.username)) {
return { error: 'Please provide a username.' };
Expand All @@ -67,8 +49,12 @@ async function handleSignIn(state: any, host) {
return { error: 'Your username must be 1-48 characters or digits.' };
}

// NOTE(jim) We've added a new scheme to keep things safe for users.
state.passwordHash = await Crypto.attemptHashWithSalt(state.password);
if (state.adminLogin == 'true') {
state.passwordHash = state.password;
} else {
// NOTE(jim) We've added a new scheme to keep things safe for users.
state.passwordHash = await Crypto.attemptHashWithSalt(state.password);
}

let r = await fetch(`${host}/login`, {
method: 'POST',
Expand Down Expand Up @@ -131,12 +117,7 @@ async function handleSignIn(state: any, host) {
}

function SignInPage(props: any) {
const [state, setState] = React.useState({ loading: false, authLoading: false, fissionLoading: false, username: '', password: '', key: '' });

const authorise = null;
const authScenario = null;
const signIn = null;

const [state, setState] = React.useState({ loading: false, authLoading: false, fissionLoading: false, username: '', password: '', adminLogin: 'false' });
return (
<Page title="Estuary: Sign in" description="Sign in to your Estuary account." url={`${props.hostname}/sign-in`}>
<Navigation active="SIGN_IN" />
Expand Down Expand Up @@ -171,6 +152,20 @@ function SignInPage(props: any) {
}}
/>

<div className={styles.actions} style={{ marginTop: '16px' }}>
<input
type="checkbox"
onClick={() => {
if (state.adminLogin === 'false') {
setState({ ...state, adminLogin: 'true' });
} else {
setState({ ...state, adminLogin: 'false' });
}
}}
/>
<H4>This user was created using estuary CLI</H4>
</div>

<div className={styles.actions}>
<Button
style={{ width: '100%' }}
Expand Down Expand Up @@ -198,40 +193,6 @@ function SignInPage(props: any) {
Create an account instead
</Button>
</div>

<H3 style={{ marginTop: 32 }}>Authenticate Using Key</H3>
<P style={{ marginTop: 8 }}>You can authenticate using an API key if you have one.</P>

<H4 style={{ marginTop: 32 }}>API key</H4>
<Input
style={{ marginTop: 8 }}
placeholder="ex: ESTxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxARY"
name="key"
value={state.key}
onChange={(e) => setState({ ...state, [e.target.name]: e.target.value.trim() })}
/>

<div className={styles.actions}>
<Button
style={{ width: '100%' }}
loading={state.authLoading ? state.authLoading : undefined}
onClick={async () => {
setState({ ...state, authLoading: true });
if (U.isEmpty(state.key)) {
alert('Please enter an API key');
setState({ ...state, authLoading: false });
return;
}
await handleTokenAuthenticate(state, props.api).then((response) => {
if (response.status == 403) {
setState({ ...state, authLoading: false });
}
});
}}
>
Authenticate
</Button>
</div>
</SingleColumnLayout>
</Page>
);
Expand Down

0 comments on commit e93b6a3

Please sign in to comment.