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(web,server): support multi-tenant login #1102

Merged
merged 7 commits into from
Mar 18, 2024
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
1 change: 1 addition & 0 deletions server/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ require (
github.com/hallazzang/echo-compose v1.0.1
github.com/jarcoal/httpmock v1.3.1
github.com/joho/godotenv v1.5.1
github.com/k0kubun/pp/v3 v3.2.0
github.com/kelseyhightower/envconfig v1.4.0
github.com/labstack/echo/v4 v4.11.4
github.com/oapi-codegen/runtime v1.1.1
Expand Down
2 changes: 2 additions & 0 deletions server/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF
github.com/jpillora/opts v1.2.3 h1:Q0YuOM7y0BlunHJ7laR1TUxkUA7xW8A2rciuZ70xs8g=
github.com/jpillora/opts v1.2.3/go.mod h1:7p7X/vlpKZmtaDFYKs956EujFqA6aCrOkcCaS6UBcR4=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
github.com/k0kubun/pp/v3 v3.2.0 h1:h33hNTZ9nVFNP3u2Fsgz8JXiF5JINoZfFq4SvKJwNcs=
github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapdz1EwA=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
Expand Down
150 changes: 84 additions & 66 deletions server/internal/app/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strings"

"github.com/joho/godotenv"
"github.com/k0kubun/pp/v3"
"github.com/kelseyhightower/envconfig"
"github.com/reearth/reearth-cms/server/internal/infrastructure/aws"
"github.com/reearth/reearth-cms/server/internal/infrastructure/gcp"
Expand All @@ -17,51 +18,55 @@ import (

const configPrefix = "REEARTH_CMS"

func init() {
pp.Default.SetColoringEnabled(false)
}

type Config struct {
Port string `default:"8080" envconfig:"PORT"`
ServerHost string
Host string `default:"http://localhost:8080"`
Dev bool
Host_Web string
GraphQL GraphQLConfig
Origins []string
DB string `default:"mongodb://localhost"`
Mailer string
SMTP SMTPConfig
SendGrid SendGridConfig
SignupSecret string
GCS GCSConfig
S3 S3Config
Task gcp.TaskConfig
AWSTask aws.TaskConfig
AssetBaseURL string
Web map[string]string
Web_Config JSON
Web_Disabled bool
Port string `default:"8080" envconfig:"PORT"`
ServerHost string `pp:",omitempty"`
Host string `default:"http://localhost:8080"`
Dev bool `pp:",omitempty"`
Host_Web string `pp:",omitempty"`
GraphQL GraphQLConfig `pp:",omitempty"`
Origins []string `pp:",omitempty"`
DB string `default:"mongodb://localhost"`
Mailer string `pp:",omitempty"`
SMTP SMTPConfig `pp:",omitempty"`
SendGrid SendGridConfig `pp:",omitempty"`
SignupSecret string `pp:",omitempty"`
GCS GCSConfig `pp:",omitempty"`
S3 S3Config `pp:",omitempty"`
Task gcp.TaskConfig `pp:",omitempty"`
AWSTask aws.TaskConfig `pp:",omitempty"`
AssetBaseURL string `pp:",omitempty"`
Web map[string]string `pp:",omitempty"`
Web_Config JSON `pp:",omitempty"`
Web_Disabled bool `pp:",omitempty"`
// auth
Auth AuthConfigs
Auth0 Auth0Config
Cognito CognitoConfig
Auth_ISS string
Auth_AUD string
Auth_ALG *string
Auth_TTL *int
Auth_ClientID *string
Auth_JWKSURI *string
Auth AuthConfigs `pp:",omitempty"`
Auth0 Auth0Config `pp:",omitempty"`
Cognito CognitoConfig `pp:",omitempty"`
Auth_ISS string `pp:",omitempty"`
Auth_AUD string `pp:",omitempty"`
Auth_ALG *string `pp:",omitempty"`
Auth_TTL *int `pp:",omitempty"`
Auth_ClientID *string `pp:",omitempty"`
Auth_JWKSURI *string `pp:",omitempty"`
// auth for m2m
AuthM2M AuthM2MConfig
AuthM2M AuthM2MConfig `pp:",omitempty"`

DB_Account string
DB_Users []appx.NamedURI
DB_Account string `pp:",omitempty"`
DB_Users []appx.NamedURI `pp:",omitempty"`
}

type AuthConfig struct {
ISS string
AUD []string
ALG *string
TTL *int
ClientID *string
JWKSURI *string
ISS string `pp:",omitempty"`
AUD []string `pp:",omitempty"`
ALG *string `pp:",omitempty"`
TTL *int `pp:",omitempty"`
ClientID *string `pp:",omitempty"`
JWKSURI *string `pp:",omitempty"`
}

type GraphQLConfig struct {
Expand All @@ -71,50 +76,50 @@ type GraphQLConfig struct {
type AuthConfigs []AuthConfig

type Auth0Config struct {
Domain string
Audience string
ClientID string
ClientSecret string
WebClientID string
Domain string `pp:",omitempty"`
Audience string `pp:",omitempty"`
ClientID string `pp:",omitempty"`
ClientSecret string `pp:",omitempty"`
WebClientID string `pp:",omitempty"`
}

type CognitoConfig struct {
UserPoolID string
Region string
ClientID string
UserPoolID string `pp:",omitempty"`
Region string `pp:",omitempty"`
ClientID string `pp:",omitempty"`
}

type SendGridConfig struct {
Email string
Name string
API string
Email string `pp:",omitempty"`
Name string `pp:",omitempty"`
API string `pp:",omitempty"`
}

type SMTPConfig struct {
Host string
Port string
SMTPUsername string
Email string
Password string
Host string `pp:",omitempty"`
Port string `pp:",omitempty"`
SMTPUsername string `pp:",omitempty"`
Email string `pp:",omitempty"`
Password string `pp:",omitempty"`
}

type GCSConfig struct {
BucketName string
PublicationCacheControl string
BucketName string `pp:",omitempty"`
PublicationCacheControl string `pp:",omitempty"`
}

type S3Config struct {
BucketName string
PublicationCacheControl string
BucketName string `pp:",omitempty"`
PublicationCacheControl string `pp:",omitempty"`
}

type AuthM2MConfig struct {
ISS string
AUD []string
ALG *string
TTL *int
Email string
JWKSURI *string
ISS string `pp:",omitempty"`
AUD []string `pp:",omitempty"`
ALG *string `pp:",omitempty"`
TTL *int `pp:",omitempty"`
Email string `pp:",omitempty"`
JWKSURI *string `pp:",omitempty"`
}

func (c *Config) Auths() (res AuthConfigs) {
Expand Down Expand Up @@ -290,13 +295,26 @@ func ReadConfig(debug bool) (*Config, error) {
}

func (c *Config) Print() string {
s := fmt.Sprintf("%+v", c)
for _, secret := range []string{c.DB, c.Auth0.ClientSecret} {
s := pp.Sprint(c)

for _, secret := range c.secrets() {
if secret == "" {
continue
}
s = strings.ReplaceAll(s, secret, "***")
}

return s
}

func (c *Config) secrets() []string {
s := []string{
c.DB,
c.Auth0.ClientSecret,
}
for _, d := range c.DB_Users {
s = append(s, d.URI)
}
return s
}

Expand Down
10 changes: 5 additions & 5 deletions server/internal/infrastructure/gcp/config.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package gcp

type TaskConfig struct {
GCPProject string
GCPRegion string
Topic string
GCSHost string
GCSBucket string
GCPProject string `pp:",omitempty"`
GCPRegion string `pp:",omitempty"`
Topic string `pp:",omitempty"`
GCSHost string `pp:",omitempty"`
GCSBucket string `pp:",omitempty"`
DecompressorImage string `default:"reearth/reearth-cms-decompressor"`
DecompressorTopic string `default:"decompress"`
DecompressorGzipExt string `default:"gml"`
Expand Down
2 changes: 2 additions & 0 deletions web/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ dist-ssr

/storybook-static
/coverage
/reearth-config.json
.env*

#amplify-do-not-edit-begin
amplify/\#current-cloud-backend
Expand Down
4 changes: 3 additions & 1 deletion web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ import { Provider as I18nProvider } from "@reearth-cms/i18n";
const router = createBrowserRouter(
createRoutesFromElements(
<>
<Route path="/" element={<RootPage />} />
<Route index element={<RootPage />} />
<Route path="auth/*" element={<RootPage />} />
<Route path="workspace" element={<CMSPageWrapper />}>
<Route index element={<Workspace />} />
<Route path=":workspaceId" element={<Workspace />} />
<Route path=":workspaceId/account" element={<AccountSettings />} />
<Route path=":workspaceId/members" element={<Members />} />
Expand Down
15 changes: 11 additions & 4 deletions web/src/auth/Auth0Auth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { useAuth0 } from "@auth0/auth0-react";

import { logOutFromTenant } from "@reearth-cms/config";

import AuthHook from "./AuthHook";

export const errorKey = "reeartherror";
Expand All @@ -21,12 +23,17 @@ export const useAuth0Auth = (): AuthHook => {
isLoading,
error: error?.message ?? null,
getAccessToken: () => getAccessTokenSilently(),
login: () => loginWithRedirect(),
logout: () =>
logout({
login: () => {
logOutFromTenant();
return loginWithRedirect();
},
logout: () => {
logOutFromTenant();
return logout({
returnTo: error
? `${window.location.origin}?${errorKey}=${encodeURIComponent(error?.message)}`
: window.location.origin,
}),
});
},
};
};
20 changes: 13 additions & 7 deletions web/src/auth/AuthProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Auth0Provider } from "@auth0/auth0-react";
import React, { createContext, ReactNode } from "react";
import React, { createContext, ReactNode, useState } from "react";

import { getAuthInfo, getSignInCallbackUrl, logInToTenant } from "@reearth-cms/config";

import { useAuth0Auth } from "./Auth0Auth";
import AuthHook from "./AuthHook";
Expand All @@ -18,12 +20,16 @@ const CognitoWrapper = ({ children }: { children: ReactNode }) => {
};

export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const authProvider = window.REEARTH_CONFIG?.authProvider;
const [authInfo] = useState(() => {
logInToTenant(); // note that it includes side effect
return getAuthInfo();
});
const authProvider = authInfo?.authProvider;

if (authProvider === "auth0") {
const domain = window.REEARTH_CONFIG?.auth0Domain;
const clientId = window.REEARTH_CONFIG?.auth0ClientId;
const audience = window.REEARTH_CONFIG?.auth0Audience;
const domain = authInfo?.auth0Domain;
const clientId = authInfo?.auth0ClientId;
const audience = authInfo?.auth0Audience;

return domain && clientId ? (
<Auth0Provider
Expand All @@ -33,7 +39,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
useRefreshTokens
scope="openid profile email"
cacheLocation="localstorage"
redirectUri={window.location.origin}>
redirectUri={getSignInCallbackUrl()}>
<Auth0Wrapper>{children}</Auth0Wrapper>
</Auth0Provider>
) : null;
Expand All @@ -44,5 +50,5 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
return <CognitoWrapper>{children}</CognitoWrapper>;
}

return <>{children}</>; // or some default fallback
return null;
};
4 changes: 4 additions & 0 deletions web/src/auth/CognitoAuth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Auth } from "@aws-amplify/auth";
import { useState, useEffect } from "react";

import { logOutFromTenant } from "@reearth-cms/config";

import AuthHook from "./AuthHook";

export const useCognitoAuth = (): AuthHook => {
Expand Down Expand Up @@ -32,10 +34,12 @@ export const useCognitoAuth = (): AuthHook => {
};

const login = () => {
logOutFromTenant();
Auth.federatedSignIn();
};

const logout = async () => {
logOutFromTenant();
try {
await Auth.signOut();
setUser(null);
Expand Down
30 changes: 0 additions & 30 deletions web/src/aws-config.ts

This file was deleted.

Loading
Loading