forked from digital-asset/daml
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support getting started guide on Daml Hub (digital-asset#12878)
* Support getting started guide on Daml Hub This is annoyingly complex so let me try to provide an explanation for the changes: 1. On Daml hub, we need to use a separate token for the public party and the user party. 2. This means that we need separate contexts. I cannot get access to the default context (not exposed) so annoyingly even for the user context we need to use a custom context 3. The way to get access to the public party in Daml hub is via a hook that reads it from the context. However, we cannot call that within the login callback so the way things work now that we login immediately show a "Logging in..." loading screen while we run the background query. This is actually kinda nice since it means something happens immediately after clicking login. I’m sure there are better ways of handling this, my react foo is very weak but this is what I managed to get to work. Tested locally as well as on Daml hub and both work fine. changelog_begin changelog_end . . . . . . . * s/any/void/ * fmt
- Loading branch information
1 parent
cab76a9
commit a230de2
Showing
9 changed files
with
404 additions
and
262 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
214 changes: 214 additions & 0 deletions
214
templates/create-daml-app/ui/src/components/LoginScreen.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
// Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import React, { useCallback, useState } from "react"; | ||
import { Button, Form, Grid, Header, Image, Segment } from "semantic-ui-react"; | ||
import Credentials, { PublicParty } from "../Credentials"; | ||
import Ledger from "@daml/ledger"; | ||
import { | ||
DamlHubLogin as DamlHubLoginBtn, | ||
usePublicParty, | ||
} from "@daml/hub-react"; | ||
import { authConfig, Insecure } from "../config"; | ||
import { useAuth0 } from "@auth0/auth0-react"; | ||
|
||
type Props = { | ||
onLogin: (credentials: Credentials) => void; | ||
}; | ||
|
||
/** | ||
* React component for the login screen of the `App`. | ||
*/ | ||
const LoginScreen: React.FC<Props> = ({ onLogin }) => { | ||
const login = useCallback( | ||
async (credentials: Credentials) => { | ||
onLogin(credentials); | ||
}, | ||
[onLogin], | ||
); | ||
|
||
const wrap: (c: JSX.Element) => JSX.Element = component => ( | ||
<Grid textAlign="center" style={{ height: "100vh" }} verticalAlign="middle"> | ||
<Grid.Column style={{ maxWidth: 450 }}> | ||
<Header | ||
as="h1" | ||
textAlign="center" | ||
size="huge" | ||
style={{ color: "#223668" }}> | ||
<Header.Content> | ||
Create | ||
<Image | ||
as="a" | ||
href="https://www.daml.com/" | ||
target="_blank" | ||
src="/daml.svg" | ||
alt="Daml Logo" | ||
spaced | ||
size="small" | ||
verticalAlign="bottom" | ||
/> | ||
App | ||
</Header.Content> | ||
</Header> | ||
<Form size="large" className="test-select-login-screen"> | ||
<Segment>{component}</Segment> | ||
</Form> | ||
</Grid.Column> | ||
</Grid> | ||
); | ||
|
||
const InsecureLogin: React.FC<{ auth: Insecure }> = ({ auth }) => { | ||
const [username, setUsername] = React.useState(""); | ||
|
||
const handleLogin = async (event: React.FormEvent) => { | ||
event.preventDefault(); | ||
const token = auth.makeToken(username); | ||
const ledger = new Ledger({ token: token }); | ||
const primaryParty: string = await auth.userManagement | ||
.primaryParty(username, ledger) | ||
.catch(error => { | ||
const errorMsg = | ||
error instanceof Error ? error.toString() : JSON.stringify(error); | ||
alert(`Failed to login as '${username}':\n${errorMsg}`); | ||
throw error; | ||
}); | ||
|
||
const useGetPublicParty = (): PublicParty => { | ||
const [publicParty, setPublicParty] = useState<string | undefined>( | ||
undefined, | ||
); | ||
const setup = () => { | ||
const fn = async () => { | ||
const publicParty = await auth.userManagement | ||
.publicParty(username, ledger) | ||
.catch(error => { | ||
const errorMsg = | ||
error instanceof Error | ||
? error.toString() | ||
: JSON.stringify(error); | ||
alert( | ||
`Failed to find primary party for user '${username}':\n${errorMsg}`, | ||
); | ||
throw error; | ||
}); | ||
// todo stop yolowing error handling | ||
setPublicParty(publicParty); | ||
}; | ||
fn(); | ||
}; | ||
return { usePublicParty: () => publicParty, setup: setup }; | ||
}; | ||
await login({ | ||
user: { userId: username, primaryParty: primaryParty }, | ||
party: primaryParty, | ||
token: auth.makeToken(username), | ||
getPublicParty: useGetPublicParty, | ||
}); | ||
}; | ||
|
||
return wrap( | ||
<> | ||
{/* FORM_BEGIN */} | ||
<Form.Input | ||
fluid | ||
placeholder="Username" | ||
value={username} | ||
className="test-select-username-field" | ||
onChange={(e, { value }) => setUsername(value?.toString() ?? "")} | ||
/> | ||
<Button | ||
primary | ||
fluid | ||
className="test-select-login-button" | ||
onClick={handleLogin}> | ||
Log in | ||
</Button> | ||
{/* FORM_END */} | ||
</>, | ||
); | ||
}; | ||
|
||
const DamlHubLogin: React.FC = () => | ||
wrap( | ||
<DamlHubLoginBtn | ||
onLogin={creds => { | ||
if (creds) { | ||
login({ | ||
party: creds.party, | ||
user: { userId: creds.partyName, primaryParty: creds.party }, | ||
token: creds.token, | ||
getPublicParty: () => ({ | ||
usePublicParty: () => usePublicParty(), | ||
setup: () => {}, | ||
}), | ||
}); | ||
} | ||
}} | ||
options={{ | ||
method: { | ||
button: { | ||
render: () => <Button primary fluid />, | ||
}, | ||
}, | ||
}} | ||
/>, | ||
); | ||
|
||
const Auth0Login: React.FC = () => { | ||
const { | ||
user, | ||
isAuthenticated, | ||
isLoading, | ||
loginWithRedirect, | ||
getAccessTokenSilently, | ||
} = useAuth0(); | ||
(async function () { | ||
if (isLoading === false && isAuthenticated === true) { | ||
if (user !== undefined) { | ||
const party = user["https://daml.com/ledger-api"]; | ||
const creds: Credentials = { | ||
user: { | ||
userId: user.email ?? user.name ?? party, | ||
primaryParty: party, | ||
}, | ||
party: party, | ||
token: await getAccessTokenSilently({ | ||
audience: "https://daml.com/ledger-api", | ||
}), | ||
getPublicParty: () => { | ||
throw Error("FIXME"); | ||
}, | ||
}; | ||
login(creds); | ||
} | ||
} | ||
})(); | ||
return wrap( | ||
<Button | ||
primary | ||
fluid | ||
className="test-select-login-button" | ||
disabled={isLoading || isAuthenticated} | ||
loading={isLoading || isAuthenticated} | ||
onClick={loginWithRedirect}> | ||
Log in | ||
</Button>, | ||
); | ||
}; | ||
|
||
if (authConfig.provider === "none") { | ||
} else if (authConfig.provider === "daml-hub") { | ||
} else if (authConfig.provider === "auth0") { | ||
} | ||
return authConfig.provider === "none" ? ( | ||
<InsecureLogin auth={authConfig} /> | ||
) : authConfig.provider === "daml-hub" ? ( | ||
<DamlHubLogin /> | ||
) : authConfig.provider === "auth0" ? ( | ||
<Auth0Login /> | ||
) : ( | ||
<div>Invalid configuation.</div> | ||
); | ||
}; | ||
|
||
export default LoginScreen; |
Oops, something went wrong.