Skip to content

Commit

Permalink
SSO for GitHub Enterprise self hosted (ToolJet#3352)
Browse files Browse the repository at this point in the history
* SSO for GitHub Enterprise self hosted

* changes

* test cases

* fixes

* label fix

* fixes

* readme changes
  • Loading branch information
gsmithun4 authored Jul 29, 2022
1 parent 0055773 commit b710d7b
Show file tree
Hide file tree
Showing 17 changed files with 556 additions and 49 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,6 @@ ENABLE_MULTIPLAYER_EDITING=true
SSO_GOOGLE_OAUTH2_CLIENT_ID=
SSO_GIT_OAUTH2_CLIENT_ID=
SSO_GIT_OAUTH2_CLIENT_SECRET=
SSO_GIT_OAUTH2_HOST=
SSO_ACCEPTED_DOMAINS=
SSO_DISABLE_SIGNUPS=
1 change: 1 addition & 0 deletions deploy/docker/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ SSO_RESTRICTED_DOMAIN=
SSO_GOOGLE_OAUTH2_CLIENT_ID=
SSO_GIT_OAUTH2_CLIENT_ID=
SSO_GIT_OAUTH2_CLIENT_SECRET=
SSO_GIT_OAUTH2_HOST=

#TELEMETRY
DEPLOYMENT_PLATFORM=docker
1 change: 1 addition & 0 deletions docs/docs/setup/env-vars.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ Configurations for instance level SSO. Valid only if `DISABLE_MULTI_WORKSPACE` i
| SSO_GOOGLE_OAUTH2_CLIENT_ID | Google OAuth client id |
| SSO_GIT_OAUTH2_CLIENT_ID | GitHub OAuth client id |
| SSO_GIT_OAUTH2_CLIENT_SECRET | GitHub OAuth client secret |
| SSO_GIT_OAUTH2_HOST | GitHub OAuth host name if GitHub is self hosted |
| SSO_ACCEPTED_DOMAINS | comma separated email domains that supports SSO authentication |
| SSO_DISABLE_SIGNUPS | Disable user sign up if authenticated user does not exist |

Expand Down
15 changes: 15 additions & 0 deletions docs/docs/user-authentication/sso/github.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,18 @@ Go to [GitHub Developer settings](https://github.com/settings/developers) and na
Lastly, enter `Client Id` and `Client Secret` in GitHub manage SSO page and save.

The GitHub sign-in button will now be available in your ToolJet login screen if you have not enabled Multi-Workspace.

:::info
Should configure `Host Name` if you are using GitHub Enterprise self hosted. Host name should be a URL and should not ends with `/`, example: `https://github.tooljet.com`
:::

## Multi-Workspace
If you have enabled Multi-Workspace you can configure GitHub SSO as mentioned above, for setting default SSO for the instance use environment variable.

| variable | description |
| ------------------------------------- | ----------------------------------------------------------- |
| SSO_GIT_OAUTH2_CLIENT_ID | GitHub OAuth client id |
| SSO_GIT_OAUTH2_CLIENT_SECRET | GitHub OAuth client secret |
| SSO_GIT_OAUTH2_HOST | GitHub OAuth host name if GitHub is self hosted |

Redirect URL should be `<host>/sso/git`
9 changes: 9 additions & 0 deletions docs/docs/user-authentication/sso/google.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,12 @@ Set the `Redirect URL` generated at manage SSO `Google` page under Authorised re
Lastly, set the `client id` in google manage SSO page. This value will be available from your [Google cloud console credentials page](https://console.cloud.google.com/apis/credentials)

The Google sign-in button will now be available in your ToolJet login screen, if you are not enabled Multi-Workspace.

## Multi-Workspace
If you have enabled Multi-Workspace you can configure Google SSO as mentioned above, for setting default SSO for the instance use environment variable.

| variable | description |
| ------------------------------------- | ----------------------------------------------------------- |
| SSO_GOOGLE_OAUTH2_CLIENT_ID | Google OAuth client id |

Redirect URL should be `<host>/sso/google`
Binary file modified docs/static/img/sso/git/manage-sso-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/static/img/sso/git/manage-sso-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion frontend/ee/components/LoginPage/GitSSOLoginButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { buildURLWithQuery } from '@/_helpers/utils';
export default function GitSSOLoginButton({ configs, text }) {
const gitLogin = (e) => {
e.preventDefault();
window.location.href = buildURLWithQuery('https://github.com/login/oauth/authorize', {
window.location.href = buildURLWithQuery(`${configs.host_name || 'https://github.com'}/login/oauth/authorize`, {
client_id: configs?.client_id,
scope: 'user:email',
});
Expand Down
1 change: 1 addition & 0 deletions frontend/src/LoginPage/LoginPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class LoginPage extends React.Component {
enabled: !!window.public_config?.SSO_GIT_OAUTH2_CLIENT_ID,
configs: {
client_id: window.public_config?.SSO_GIT_OAUTH2_CLIENT_ID,
host_name: window.public_config?.SSO_GIT_OAUTH2_HOST,
},
},
form: {
Expand Down
27 changes: 25 additions & 2 deletions frontend/src/ManageSSO/Git.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import { copyToClipboard } from '@/_helpers/appUtils';
export function Git({ settings, updateData }) {
const [enabled, setEnabled] = useState(settings?.enabled || false);
const [clientId, setClientId] = useState(settings?.configs?.client_id || '');
const [hostName, setHostName] = useState(settings?.configs?.host_name || '');
const [clientSecret, setClientSecret] = useState(settings?.configs?.client_secret || '');
const [isSaving, setSaving] = useState(false);
const [configId, setConfigId] = useState(settings?.id);

const reset = () => {
setClientId(settings?.configs?.client_id || '');
setClientSecret(settings?.configs?.client_secret || '');
setHostName(settings?.configs?.host_name || '');
};

const copyFunction = (input) => {
Expand All @@ -21,11 +23,14 @@ export function Git({ settings, updateData }) {
};
const saveSettings = () => {
setSaving(true);
organizationService.editOrganizationConfigs({ type: 'git', configs: { clientId, clientSecret } }).then(
organizationService.editOrganizationConfigs({ type: 'git', configs: { clientId, clientSecret, hostName } }).then(
(data) => {
setSaving(false);
data.id && setConfigId(data.id);
updateData('git', { id: data.id, configs: { client_id: clientId, client_secret: clientSecret } });
updateData('git', {
id: data.id,
configs: { client_id: clientId, client_secret: clientSecret, host_name: hostName },
});
toast.success('updated SSO configurations', {
position: 'top-center',
});
Expand Down Expand Up @@ -86,6 +91,24 @@ export function Git({ settings, updateData }) {
</div>
<div className="card-body">
<form noValidate>
<div className="form-group mb-3">
<label className="form-label" data-cy="host-name-label">
Host Name
</label>
<div>
<input
type="text"
className="form-control"
placeholder="Enter Host Name"
value={hostName}
onChange={(e) => setHostName(e.target.value)}
data-cy="host-name-input"
/>
</div>
<div className="help-text mt-2">
<div data-cy="general-settings-help-text">Required if GitHub is self hosted</div>
</div>
</div>
<div className="form-group mb-3">
<label className="form-label" data-cy="client-id-label">
Client Id
Expand Down
29 changes: 17 additions & 12 deletions server/ee/services/oauth/git_oauth.service.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import got from 'got';
import UserResponse from './models/user_response';

@Injectable()
export class GitOAuthService {
constructor(private readonly configService: ConfigService) {}
private readonly authUrl = 'https://github.com/login/oauth/access_token';
private readonly getUserUrl = 'https://api.github.com/user';
private readonly getUserEmailUrl = 'https://api.github.com/user/emails';
private readonly authUrl = '/login/oauth/access_token';

async #getUserDetails({ access_token }: AuthResponse): Promise<UserResponse> {
const response: any = await got(this.getUserUrl, {
#getAuthUrl(hostName) {
return `${hostName || 'https://github.com'}${this.authUrl}`;
}
#getUserUrl(hostName) {
return `${hostName ? `${hostName}/api/v3` : 'https://github.com'}/user`;
}
#getUserEmailUrl(hostName) {
return `${hostName ? `${hostName}/api/v3` : 'https://github.com'}/user/emails`;
}
async #getUserDetails({ access_token }: AuthResponse, hostName: string): Promise<UserResponse> {
const response: any = await got(this.#getUserUrl(hostName), {
method: 'get',
headers: { Accept: 'application/json', Authorization: `token ${access_token}` },
}).json();
Expand All @@ -24,14 +29,14 @@ export class GitOAuthService {

if (!email) {
// email visibility not set to public
email = await this.#getEmailId(access_token);
email = await this.#getEmailId(access_token, hostName);
}

return { userSSOId: access_token, firstName, lastName, email, sso: 'git' };
}

async #getEmailId(access_token: string) {
const response: any = await got(this.getUserEmailUrl, {
async #getEmailId(access_token: string, hostName: string) {
const response: any = await got(this.#getUserEmailUrl(hostName), {
method: 'get',
headers: { Accept: 'application/json', Authorization: `token ${access_token}` },
}).json();
Expand All @@ -40,13 +45,13 @@ export class GitOAuthService {
}

async signIn(code: string, configs: any): Promise<any> {
const response: any = await got(this.authUrl, {
const response: any = await got(this.#getAuthUrl(configs.hostName), {
method: 'post',
headers: { Accept: 'application/json' },
json: { client_id: configs.clientId, client_secret: configs.clientSecret, code },
}).json();

return await this.#getUserDetails(response);
return await this.#getUserDetails(response, configs.hostName);
}
}

Expand Down
1 change: 1 addition & 0 deletions server/ee/services/oauth/oauth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ export class OauthService {
configs: {
clientId: this.configService.get<string>('SSO_GIT_OAUTH2_CLIENT_ID'),
clientSecret: this.configService.get<string>('SSO_GIT_OAUTH2_CLIENT_SECRET'),
hostName: this.configService.get<string>('SSO_GIT_OAUTH2_HOST'),
},
};
default:
Expand Down
1 change: 1 addition & 0 deletions server/src/entities/sso_config.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type Google = {
type Git = {
clientId: string;
clientSecret: string;
hostName?: string;
};
@Entity({ name: 'sso_configs' })
export class SSOConfigs {
Expand Down
1 change: 1 addition & 0 deletions server/src/services/app_config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export class AppConfigService {
'DISABLE_MULTI_WORKSPACE',
'SSO_GOOGLE_OAUTH2_CLIENT_ID',
'SSO_GIT_OAUTH2_CLIENT_ID',
'SSO_GIT_OAUTH2_HOST',
'SSO_DISABLE_SIGNUPS',
];
}
Expand Down
6 changes: 6 additions & 0 deletions server/src/services/organizations.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,12 @@ export class OrganizationsService {
enabled: true,
configs: {
clientId: this.configService.get<string>('SSO_GIT_OAUTH2_CLIENT_ID'),
clientSecret: await this.encryptionService.encryptColumnValue(
'ssoConfigs',
'clientSecret',
this.configService.get<string>('SSO_GIT_OAUTH2_CLIENT_SECRET')
),
hostName: this.configService.get<string>('SSO_GIT_OAUTH2_HOST'),
},
});
}
Expand Down
Loading

0 comments on commit b710d7b

Please sign in to comment.