Skip to content

OAuth updates for third party login options. #389

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

Merged
merged 7 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
login/register through third party oauth
  • Loading branch information
raheeliftikhar5 committed Sep 21, 2023
commit 726b77fffd021dfa1c881102f89a5f9622fe30a4
10 changes: 7 additions & 3 deletions client/packages/lowcoder/src/api/configApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ export interface ConfigResponse extends ApiResponse {
}

class ConfigApi extends Api {
static configURL = "/v1/configs";
static configURL = "/configs";

static fetchConfig(): AxiosPromise<ConfigResponse> {
return Api.get(ConfigApi.configURL);
static fetchConfig(orgId?: string): AxiosPromise<ConfigResponse> {
let authConfigURL = ConfigApi.configURL;
if(orgId?.length) {
authConfigURL += `?orgId?=${orgId}`;
}
return Api.get(authConfigURL);
}
}

Expand Down
3 changes: 2 additions & 1 deletion client/packages/lowcoder/src/api/inviteApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ export type InviteInfo = {
inviteCode: string;
createUserName: string;
invitedOrganizationName: string;
invitedOrganizationId: string;
};

class InviteApi extends Api {
static getInviteURL = "/v1/invitation";
static getInviteURL = "/invitation";
static acceptInviteURL = (invitationId: string) => `/v1/invitation/${invitationId}/invite`;

// generate invitation
Expand Down
5 changes: 3 additions & 2 deletions client/packages/lowcoder/src/api/userApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface CommonLoginParam {
invitationId?: string;
authId?: string;
source?: string;
orgId?: string;
}

export interface CommonBindParam {
Expand All @@ -17,8 +18,8 @@ export interface CommonBindParam {
source?: string;
}

interface ThirdPartyAuthRequest {
state: string;
export interface ThirdPartyAuthRequest {
state?: string;
code: string;
redirectUrl: string;
}
Expand Down
4 changes: 3 additions & 1 deletion client/packages/lowcoder/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ type AppIndexProps = {
class AppIndex extends React.Component<AppIndexProps, any> {
componentDidMount() {
this.props.getCurrentUser();
this.props.fetchConfig();
if (!history.location.pathname.startsWith("/invite/")) {
this.props.fetchConfig();
}
if (history.location.pathname === BASE_URL) {
this.props.fetchHome();
}
Expand Down
3 changes: 2 additions & 1 deletion client/packages/lowcoder/src/constants/authConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export type AuthSessionStoreParams = {
afterLoginRedirect: string | null;
sourceType: string;
invitationId?: string;
invitedOrganizationId?: string;
routeLink?: boolean;
name: string;
authId?: string;
Expand All @@ -65,7 +66,7 @@ export type AuthSessionStoreParams = {
* action after third party auth
* bind & innerBind has different redirect action
*/
export type ThirdPartyAuthGoal = "login" | "bind" | "innerBind";
export type ThirdPartyAuthGoal = "register" | "login" | "bind" | "innerBind";

export const AuthRoutes: Array<{ path: string; component: React.ComponentType<any> }> = [
{ path: AUTH_LOGIN_URL, component: Login },
Expand Down
17 changes: 14 additions & 3 deletions client/packages/lowcoder/src/pages/common/inviteLanding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@ import { RouteComponentProps } from "react-router-dom";
import { AppState } from "redux/reducers";
import history from "util/history";
import { isFetchUserFinished } from "redux/selectors/usersSelectors";
import { fetchConfigAction } from "redux/reduxActions/configActions";
import { trans } from "i18n";
import { messageInstance } from "lowcoder-design";

type InviteLandingProp = RouteComponentProps<{ invitationId: string }, StaticContext, any> & {
invitationId: string;
fetchConfig: (orgId?: string) => void;
};

function InviteLanding(props: InviteLandingProp) {
const { invitationId } = props;
const { invitationId, fetchConfig } = props;
const fetchUserFinished = useSelector(isFetchUserFinished);
useEffect(() => {
if (!fetchUserFinished) {
Expand All @@ -27,6 +29,7 @@ function InviteLanding(props: InviteLandingProp) {
history.push(BASE_URL);
return;
}
let orgId:string | undefined = undefined;
// accept the invitation
InviteApi.acceptInvite({ invitationId })
.then((resp) => {
Expand All @@ -39,6 +42,7 @@ function InviteLanding(props: InviteLandingProp) {
resp?.status === API_STATUS_CODES.REQUEST_NOT_AUTHORISED
) {
const inviteInfo = resp.data.data;
orgId = inviteInfo.invitedOrganizationId;
const inviteState = inviteInfo ? { ...inviteInfo, invitationId } : { invitationId };
history.push({
pathname: AUTH_LOGIN_URL,
Expand All @@ -53,8 +57,10 @@ function InviteLanding(props: InviteLandingProp) {
.catch((errorResp) => {
messageInstance.error(errorResp.message);
history.push(BASE_URL);
}).finally(() => {
fetchConfig(orgId);
});
}, [fetchUserFinished, invitationId]);
}, [fetchUserFinished, invitationId, fetchConfig]);
return null;
}

Expand All @@ -64,4 +70,9 @@ const mapStateToProps = (state: AppState, props: InviteLandingProp) => {
};
};

export default connect(mapStateToProps)(InviteLanding);
const mapDispatchToProps = (dispatch: any) => ({
fetchConfig: (orgId?: string) => dispatch(fetchConfigAction(orgId)),
});


export default connect(mapStateToProps, mapDispatchToProps)(InviteLanding);
17 changes: 15 additions & 2 deletions client/packages/lowcoder/src/pages/userAuth/authComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,23 @@ export const LoginLogoStyle = styled.img`
margin-right: 8px;
width: 32px;
height: 32px;
position: absolute;
left: 6px;
`;

export const StyledLoginButton = styled.button`
padding: 0;
export const LoginLabelStyle = styled.p`
font-size: 16px;
color: #333333;
line-height: 16px;
margin: 0px;
`;

export const StyledLoginButton = styled(TacoButton)`
position: relative;
height: 48px;
border: 1px solid lightgray !important;
border-radius: 8px;
padding: 8px;
white-space: nowrap;
word-break: keep-all;
outline: 0;
Expand Down
4 changes: 3 additions & 1 deletion client/packages/lowcoder/src/pages/userAuth/authUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ export const geneAuthStateAndSaveParam = (
authGoal: ThirdPartyAuthGoal,
config: ThirdPartyConfigType,
afterLoginRedirect: string | null,
invitationId?: string
invitationId?: string,
invitedOrganizationId?: string,
) => {
const state = Math.floor(Math.random() * 0xffffffff).toString(16);
const params: AuthSessionStoreParams = {
Expand All @@ -135,6 +136,7 @@ export const geneAuthStateAndSaveParam = (
afterLoginRedirect: afterLoginRedirect || null,
sourceType: config.sourceType,
invitationId: invitationId,
invitedOrganizationId: invitedOrganizationId,
routeLink: config.routeLink,
name: config.name,
authId: config.id,
Expand Down
13 changes: 12 additions & 1 deletion client/packages/lowcoder/src/pages/userAuth/formLogin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { AuthContext, useAuthSubmit } from "pages/userAuth/authUtils";
import { ThirdPartyAuth } from "pages/userAuth/thirdParty/thirdPartyAuth";
import { AUTH_REGISTER_URL } from "constants/routesURL";
import { useLocation } from "react-router-dom";
import { Divider } from "antd";

const AccountLoginWrapper = styled(FormWrapperMobile)`
display: flex;
Expand All @@ -30,6 +31,7 @@ export default function FormLogin() {
const redirectUrl = useRedirectUrl();
const { systemConfig, inviteInfo } = useContext(AuthContext);
const invitationId = inviteInfo?.invitationId;
const invitedOrganizationId = inviteInfo?.invitedOrganizationId;
const authId = systemConfig?.form.id;
const location = useLocation();

Expand Down Expand Up @@ -69,9 +71,18 @@ export default function FormLogin() {
<ConfirmButton loading={loading} disabled={!account || !password} onClick={onSubmit}>
{trans("userAuth.login")}
</ConfirmButton>
{Boolean(invitationId) && (
<>
<Divider />
<ThirdPartyAuth
invitationId={invitationId}
invitedOrganizationId={invitedOrganizationId}
authGoal="login"
/>
</>
)}
</AccountLoginWrapper>
<AuthBottomView>
<ThirdPartyAuth invitationId={invitationId} authGoal="login" />
{systemConfig.form.enableRegister && (
<StyledRouteLink to={{ pathname: AUTH_REGISTER_URL, state: location.state }}>
{trans("userAuth.register")}
Expand Down
36 changes: 21 additions & 15 deletions client/packages/lowcoder/src/pages/userAuth/register.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import { useLocation } from "react-router-dom";
import { UserConnectionSource } from "@lowcoder-ee/constants/userConstants";
import { trans } from "i18n";
import { AuthContext, checkPassWithMsg, useAuthSubmit } from "pages/userAuth/authUtils";
import { Divider } from "antd";
import { ThirdPartyAuth } from "pages/userAuth/thirdParty/thirdPartyAuth";

const StyledFormInput = styled(FormInput)`
margin-bottom: 16px;
Expand All @@ -30,16 +32,10 @@ const StyledPasswordInput = styled(PasswordInput)`
const RegisterContent = styled(FormWrapperMobile)`
display: flex;
flex-direction: column;
margin-bottom: 106px;

button {
margin: 20px 0 16px 0;
}
`;

const TermsAndPrivacyInfoWrapper = styled.div`
margin-bottom: 80px;
@media screen and (max-width: 640px) {
margin: 10px 0 64px 0;
margin-bottom: 16px;
}
`;

Expand All @@ -50,14 +46,16 @@ function UserRegister() {
const redirectUrl = useRedirectUrl();
const location = useLocation();
const { systemConfig, inviteInfo } = useContext(AuthContext);
const invitationId = inviteInfo?.invitationId;
const invitedOrganizationId = inviteInfo?.invitedOrganizationId;
const authId = systemConfig.form.id;
const { loading, onSubmit } = useAuthSubmit(
() =>
UserApi.formLogin({
register: true,
loginId: account,
password: password,
invitationId: inviteInfo?.invitationId,
invitationId,
source: UserConnectionSource.email,
authId,
}),
Expand Down Expand Up @@ -95,13 +93,21 @@ function UserRegister() {
>
{trans("userAuth.register")}
</ConfirmButton>
<TermsAndPrivacyInfoWrapper>
<TermsAndPrivacyInfo onCheckChange={(e) => setSubmitBtnDisable(!e.target.checked)} />
</TermsAndPrivacyInfoWrapper>
<StyledRouteLinkLogin to={{ pathname: AUTH_LOGIN_URL, state: location.state }}>
{trans("userAuth.userLogin")}
</StyledRouteLinkLogin>
<TermsAndPrivacyInfo onCheckChange={(e) => setSubmitBtnDisable(!e.target.checked)} />
{Boolean(invitationId) && (
<>
<Divider />
<ThirdPartyAuth
invitationId={invitationId}
invitedOrganizationId={invitedOrganizationId}
authGoal="register"
/>
</>
)}
</RegisterContent>
<StyledRouteLinkLogin to={{ pathname: AUTH_LOGIN_URL, state: location.state }}>
{trans("userAuth.userLogin")}
</StyledRouteLinkLogin>
</AuthContainer>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useLocation } from "react-router-dom";
import { AuthSessionStoreParams } from "constants/authConstants";
import { messageInstance } from "lowcoder-design";

import { AUTH_LOGIN_URL, BASE_URL } from "constants/routesURL";
import { AUTH_LOGIN_URL, AUTH_REGISTER_URL, BASE_URL } from "constants/routesURL";
import history from "util/history";
import PageSkeleton from "components/PageSkeleton";
import { trans } from "i18n";
Expand Down Expand Up @@ -35,7 +35,13 @@ function validateParam(authParams: AuthSessionStoreParams, urlParam: AuthRedirec
return true;
} else {
messageInstance.error(trans("userAuth.invalidThirdPartyParam"));
history.push(authParams.authGoal === "login" ? AUTH_LOGIN_URL : BASE_URL, {
let redirectUrl = BASE_URL;
if(authParams.authGoal === "login") {
redirectUrl = AUTH_LOGIN_URL;
} else if(authParams.authGoal === "register") {
redirectUrl = AUTH_REGISTER_URL;
}
history.push(redirectUrl, {
thirdPartyAuthError: true,
});
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ export abstract class AbstractAuthenticator {

doAuth() {
const { authParams } = this;
authParams.authGoal === "login" ? this.doLogin() : this.doBind();
(authParams.authGoal === "login" || authParams.authGoal === "register")
? this.doLogin()
: this.doBind();
}

protected doLogin() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AbstractAuthenticator } from "./abstractAuthenticator";
import { AxiosPromise } from "axios";
import UserApi from "api/userApi";
import UserApi, { CommonLoginParam, ThirdPartyAuthRequest } from "api/userApi";
import { ApiResponse } from "api/apiResponses";

export class OAuthAuthenticator extends AbstractAuthenticator {
Expand All @@ -19,13 +19,16 @@ export class OAuthAuthenticator extends AbstractAuthenticator {

login(): AxiosPromise<ApiResponse> {
const { urlParam, authParams, redirectUrl } = this;
return UserApi.thirdPartyLogin({
const params: ThirdPartyAuthRequest & CommonLoginParam = {
state: urlParam.state!,
code: urlParam.code!,
source: authParams.sourceType,
authId: authParams.authId,
redirectUrl: redirectUrl,
...(authParams.invitationId && { invitationId: authParams.invitationId }),
});
}
if(authParams.invitedOrganizationId) {
params.orgId = authParams.invitedOrganizationId;
}
return UserApi.thirdPartyLogin(params);
}
}
Loading