Skip to content
Open
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
6 changes: 6 additions & 0 deletions config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@
"clientID": "",
"clientSecret": ""
},
"apple": {
"clientID": "",
"teamID": "",
"keyID": "",
"privateKey": ""
},
"path": "/authn",
"service": "users"
}
Expand Down
1 change: 1 addition & 0 deletions eslint.config.shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const baseConfig = {
require: 'readonly',
global: 'readonly',
URL: 'readonly',
URLSearchParams: 'readonly',
// Browser globals
window: 'readonly',
document: 'readonly',
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

99 changes: 4 additions & 95 deletions src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,6 @@

import * as React from 'react';

import { initializeApp } from '@firebase/app';
import {
getAuth,
connectAuthEmulator,
onAuthStateChanged,
Auth as FirebaseAuth,
User as FirebaseUser,
} from '@firebase/auth';

import { useLocation, Route, RouteComponentProps, Switch, Redirect } from 'wouter';

import { defined } from '@simlin/core/common';
Expand All @@ -25,12 +16,6 @@ import { User } from './User';

import styles from './App.module.css';

const config = {
apiKey: 'AIzaSyConH72HQl9xOtjmYJO9o2kQ9nZZzl96G8',
authDomain: 'auth.simlin.com',
};
const firebaseApp = initializeApp(config);

interface EditorMatchParams {
username: string;
projectName: string;
Expand All @@ -42,8 +27,8 @@ class UserInfoSingleton {
private resultPromise?: Promise<[User | undefined, number]>;
private result?: [User | undefined, number];
constructor() {
// store this promise; we might race calling get() below, but all racers will
// await this single fetch result.
// Store this promise; we might race calling get() below, but all racers
// will await this single fetch result.
this.fetch();
}

Expand Down Expand Up @@ -75,7 +60,7 @@ class UserInfoSingleton {
this.fetch();

if (resultPromise) {
// don't leave the promise un-awaited
// Don't leave the in-flight promise un-awaited
await resultPromise;
}
}
Expand All @@ -87,8 +72,6 @@ interface AppState {
authUnknown: boolean;
isNewUser?: boolean;
user?: User;
auth: FirebaseAuth;
firebaseIdToken?: string | null;
}

class InnerApp extends React.PureComponent<{}, AppState> {
Expand All @@ -97,91 +80,19 @@ class InnerApp extends React.PureComponent<{}, AppState> {
constructor(props: {}) {
super(props);

const isDevServer = process.env.NODE_ENV === 'development';
const auth = getAuth(firebaseApp);
if (isDevServer) {
connectAuthEmulator(auth, 'http://localhost:9099', { disableWarnings: true });
}

this.state = {
authUnknown: true,
auth,
};

// notify our app when a user logs in
onAuthStateChanged(auth, this.authStateChanged);

setTimeout(this.getUserInfo);
}

authStateChanged = (user: FirebaseUser | null) => {
setTimeout(this.asyncAuthStateChanged, undefined, user);
};

asyncAuthStateChanged = async (user: FirebaseUser | null) => {
if (!user) {
this.setState({ firebaseIdToken: null });
return;
}

const firebaseIdToken = await user.getIdToken();
this.setState({ firebaseIdToken });
await this.maybeLogin(undefined, firebaseIdToken);
};

async maybeLogin(authIsKnown = false, firebaseIdToken?: string): Promise<void> {
authIsKnown = authIsKnown || !this.state.authUnknown;
if (!authIsKnown) {
return;
}

// if we know the user, we don't need to log in
const [user] = await userInfo.get();
if (user) {
return;
}

const idToken = firebaseIdToken ?? this.state.firebaseIdToken;
if (idToken === null || idToken === undefined) {
return;
}

const bodyContents = {
idToken,
};

const base = this.getBaseURL();
const apiPath = `${base}/session`;
const response = await fetch(apiPath, {
credentials: 'same-origin',
method: 'POST',
cache: 'no-cache',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(bodyContents),
});

const status = response.status;
if (!(status >= 200 && status < 400)) {
const body = await response.json();
const errorMsg =
body && body.error ? (body.error as string) : `HTTP ${status}; maybe try a different username ¯\\_(ツ)_/¯`;
// this.appendModelError(errorMsg);
console.log(`session error: ${errorMsg}`);
return undefined;
}

this.handleUsernameChanged();
}

getUserInfo = async (): Promise<void> => {
const [user, status] = await userInfo.get();
if (!(status >= 200 && status < 400) || !user) {
this.setState({
authUnknown: false,
});
await this.maybeLogin(true);
return;
}
const isNewUser = user.id.startsWith(`temp-`);
Expand Down Expand Up @@ -230,11 +141,9 @@ class InnerApp extends React.PureComponent<{}, AppState> {
const projectParam = urlParams.get('project');
if (projectParam) return <Redirect to={projectParam} />;

// if a user is navigating to a project,
// skip the high level auth check, to enable public models
if (!/\/.*\/.*/.test(window.location.pathname)) {
if (!this.state.user) {
return <Login disabled={this.state.authUnknown} auth={this.state.auth} />;
return <Login disabled={this.state.authUnknown} onLoginSuccess={this.handleUsernameChanged} />;
}

if (this.state.isNewUser) {
Expand Down
Loading
Loading