Skip to content

Commit

Permalink
Feat: The new login UI (logseq#8865)
Browse files Browse the repository at this point in the history
Built-in login UI instead of callback

---------

Co-authored-by: rcmerci <[email protected]>
Co-authored-by: Konstantinos Kaloutas <[email protected]>
Co-authored-by: Tienson Qin <[email protected]>
  • Loading branch information
4 people authored Mar 27, 2023
1 parent 2b15702 commit 95c5cba
Show file tree
Hide file tree
Showing 25 changed files with 6,356 additions and 85 deletions.
2 changes: 2 additions & 0 deletions packages/amplify/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.parcel-cache
dist
16 changes: 16 additions & 0 deletions packages/amplify/examples/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
</head>
<body>
<div id="app"></div>
<script type="module" src="./index.tsx"></script>
</body>
</html>
87 changes: 87 additions & 0 deletions packages/amplify/examples/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import { Amplify } from 'aws-amplify'
import { Authenticator } from '@aws-amplify/ui-react'
import '@aws-amplify/ui-react/styles.css'

function setupConfigure () {
Amplify.configure({
Auth: {
// REQUIRED only for Federated Authentication - Amazon Cognito Identity Pool ID
// identityPoolId: 'XX-XXXX-X:XXXXXXXX-XXXX-1234-abcd-1234567890ab',

// REQUIRED - Amazon Cognito Region
region: 'us-east-1',

// OPTIONAL - Amazon Cognito Federated Identity Pool Region
// Required only if it's different from Amazon Cognito Region
// identityPoolRegion: 'XX-XXXX-X',

// OPTIONAL - Amazon Cognito User Pool ID
userPoolId: 'us-east-1_ldvDmC9Fe',

// OPTIONAL - Amazon Cognito Web Client ID (26-char alphanumeric string)
userPoolWebClientId: '41m82unjghlea984vjpk887qcr',

// OPTIONAL - Enforce user authentication prior to accessing AWS resources or not
// mandatorySignIn: false,

// OPTIONAL - This is used when autoSignIn is enabled for Auth.signUp
// 'code' is used for Auth.confirmSignUp, 'link' is used for email link verification
// signUpVerificationMethod: 'code', // 'code' | 'link'

// OPTIONAL - Configuration for cookie storage
// Note: if the secure flag is set to true, then the cookie transmission requires a secure protocol
cookieStorage: {
domain: 'localhost',
path: '/',
expires: 365,
sameSite: 'strict',
secure: true,
},

// OPTIONAL - customized storage object
// storage: MyStorage,

// OPTIONAL - Manually set the authentication flow type. Default is 'USER_SRP_AUTH'
authenticationFlowType: 'USER_SRP_AUTH',

//
// // OPTIONAL - Manually set key value pairs that can be passed to Cognito Lambda Triggers
// clientMetadata: {myCustomKey: 'myCustomValue'},
//
// // OPTIONAL - Hosted UI configuration
// oauth: {
// domain: 'your_cognito_domain',
// scope: ['phone', 'email', 'profile', 'openid', 'aws.cognito.signin.user.admin'],
// redirectSignIn: 'http://localhost:3000/',
// redirectSignOut: 'http://localhost:3000/',
// responseType: 'code' // or 'token', note that REFRESH token will only be generated when the responseType is code
}
})
}

export default function App () {
return (
<div style={{ display: 'flex', justifyContent: 'center', height: '90vh', alignItems: 'center' }}>
<Authenticator signUpAttributes={['email']}
socialProviders={['google']}>
{({ signOut, user }) => (
<main>
<h1>Hello {user.username}</h1>
<button onClick={signOut}>Sign out</button>
</main>
)}
</Authenticator>
</div>)
}

function main () {
setupConfigure()

// mount
ReactDOM.render(<App/>, document.getElementById('app'))
}

// bootstrap
main()
42 changes: 42 additions & 0 deletions packages/amplify/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "logseq-amplify",
"version": "0.0.1",
"description": "Amplify components for Logseq",
"license": "MIT",
"scripts": {
"dev:amplify": "parcel watch ./src/amplify.ts --dist-dir ../src/main/frontend/ --no-hmr --no-source-maps",
"dev:examples": "parcel serve ./examples/index.html",
"build:amplify": "parcel build ./src/amplify.ts --dist-dir ../../resources/js --no-source-maps && mv ../../resources/js/amplify.css ../../resources/css/"
},
"devDependencies": {
"buffer": "^5.5.0",
"parcel": "^2.8.3",
"punycode": "^1.4.1"
},
"dependencies": {
"@aws-amplify/ui-react": "^4.3.8",
"aws-amplify": "^5.0.15",
"aws-amplify-react": "^5.1.43",
"react": "^17",
"react-dom": "^17"
},
"alias": {
"react": {
"global": "React"
},
"react-dom": {
"global": "ReactDOM"
},
"react/jsx-dev-runtime": "./node_modules/react/jsx-dev-runtime.js",
"react/jsx-runtime": "./node_modules/react/jsx-runtime.js"
},
"targets": {
"default": {
"outputFormat": "global",
"includeNodeModules": {
"react": false,
"react-dom": false
}
}
}
}
60 changes: 60 additions & 0 deletions packages/amplify/src/LSAuthenticator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Authenticator, CheckboxField, useAuthenticator, AccountSettings } from '@aws-amplify/ui-react'

export function LSAuthenticator({ termsLink, children }: any) {
return (<div>
<Authenticator
formFields={{
signUp: {
email: { order: 1 },
username: { order: 2 },
password: { order: 3 },
confirm_password: { order: 4 },
}
}}
loginMechanisms={['username']}
socialProviders={['google']}
components={{
SignUp: {
FormFields() {
const { validationErrors } = useAuthenticator()

return (
<>
{/* Re-use default `Authenticator.SignUp.FormFields` */}
<Authenticator.SignUp.FormFields/>

{/* Append & require Terms & Conditions field to sign up */}
<CheckboxField
errorMessage={validationErrors.acknowledgement as string}
hasError={!!validationErrors.acknowledgement}
name="acknowledgement"
value="yes"
label={(<a href={termsLink}>I agree with the Terms & Conditions</a>)}
/>
</>
)
},
},
}}
services={{
async validateCustomSignUp(formData) {
if (!formData.acknowledgement) {
return {
acknowledgement: '',
}
}
}
}}
>
{children}
</Authenticator>
</div>)
}

export function LSAuthenticatorChangePassword(
{onSuccess, onError}
) {
return (
<AccountSettings.ChangePassword onSuccess={onSuccess} onError={onError}/>
)
}
99 changes: 99 additions & 0 deletions packages/amplify/src/amplify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import '@aws-amplify/ui-react/styles.css'
import { Amplify, Auth, Hub, I18n } from 'aws-amplify'
import { LSAuthenticator, LSAuthenticatorChangePassword } from './LSAuthenticator'
import { dict } from 'aws-amplify-react/lib-esm/AmplifyI18n'

// fix i18n
dict.zh['Reset Password'] = '重置密码'
dict.zh['Enter your username'] = '请输入用户名'
dict.zh['Enter your email'] = '请输入邮箱'
dict.zh['Enter your password'] = '请输入密码'
dict.zh['Confirm Password'] = '确认密码'
dict.zh['Please confirm your Password'] = '请确认密码'
dict.zh['Incorrect username or password.'] = '用户名或者密码不正确。如果您的邮箱未验证,请尝试使用用户名(非邮箱)登录,以保证再次邮箱验证流程。'

// @ts-ignore attach defaults
dict.en = {
'Incorrect username or password.': 'Incorrect username or password! ' +
'For unconfirmed users, please input your username instead of Email to receive the code.'
}

const fixesMapping = {
'Sign Up': ['Sign up', 'Create Account'],
'Sign In': ['Sign in'],
'Sign Out': 'Sign out',
'Send Code': 'Send code',
'Forgot Password': ['Forgot your password?'],
'Enter your email': ['Enter your Email'],
'Enter your password': ['Enter your Password'],
'Enter your username': ['Enter your Username']
}

Object.keys(dict).forEach((k) => {
const target = dict[k]
Object.entries(fixesMapping).forEach(([k1, v1]) => {
if (target?.hasOwnProperty(k1)) {
const vs = Array.isArray(v1) ? v1 : [v1]
vs.forEach(it => {
target[it] = target[k1]
})
}
})
})

I18n.putVocabularies(dict)

function setupAuthConfigure(config) {

const {
region,
userPoolId,
userPoolWebClientId,
identityPoolId,
oauthDomain,
oauthProviders
} = config

Amplify.configure({
'aws_project_region': region,
'aws_cognito_identity_pool_id': identityPoolId,
'aws_cognito_region': region,
'aws_user_pools_id': userPoolId,
'aws_user_pools_web_client_id': userPoolWebClientId,
'authenticationFlowType': 'USER_SRP_AUTH',
'oauth': {
'domain': oauthDomain,
'scope': [
'phone',
'email',
'openid',
'profile',
'aws.cognito.signin.user.admin'
],
'redirectSignIn': 'https://logseq.com/public/auth_callback.html',
'redirectSignOut': 'https://logseq.com/public/auth_callback.html',
'responseType': 'code'
},
'federationTarget': 'COGNITO_USER_POOLS',
'aws_cognito_social_providers': oauthProviders || [
'GOOGLE'
],
'aws_cognito_signup_attributes': [
'EMAIL'
],
'aws_cognito_password_protection_settings': {
'passwordPolicyMinLength': 8,
'passwordPolicyCharacters': []
},
'aws_cognito_verification_mechanisms': [
'EMAIL'
]
})
}

//@ts-ignore
window.LSAmplify = {
setupAuthConfigure,
LSAuthenticator, LSAuthenticatorChangePassword,
Auth, Amplify, Hub, I18n
}
Loading

0 comments on commit 95c5cba

Please sign in to comment.