Skip to content

Commit

Permalink
Merge branch 'master' into sam/24_11_26/fix/enduser-webhooks
Browse files Browse the repository at this point in the history
  • Loading branch information
bodinsamuel authored Nov 27, 2024
2 parents 496444b + 57bac9a commit 2e0c759
Show file tree
Hide file tree
Showing 22 changed files with 239 additions and 185 deletions.
17 changes: 16 additions & 1 deletion packages/server/lib/controllers/oauth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -537,13 +537,28 @@ class OAuthController {
oauth2Client.getSimpleOAuth2ClientConfig(providerConfig, provider, connectionConfig)
);

const scopeSeparator = provider.scope_separator || ' ';
const scopes = providerConfig.oauth_scopes ? providerConfig.oauth_scopes.split(',').join(scopeSeparator) : '';

let authorizationUri = simpleOAuthClient.authorizeURL({
redirect_uri: callbackUrl,
scope: providerConfig.oauth_scopes ? providerConfig.oauth_scopes.split(',').join(provider.scope_separator || ' ') : '',
scope: scopes,
state: session.id,
...allAuthParams
});

if (provider?.authorization_url_skip_encode?.includes('scopes')) {
const url = new URL(authorizationUri);
const queryParams = new URLSearchParams(url.search);
queryParams.delete('scope');
let newQuery = queryParams.toString();
if (scopes) {
newQuery = newQuery ? `${newQuery}&scope=${scopes}` : `scope=${scopes}`;
}
url.search = newQuery;
authorizationUri = url.toString();
}

if (provider.authorization_url_fragment) {
const urlObj = new URL(authorizationUri);
const { search } = urlObj;
Expand Down
11 changes: 8 additions & 3 deletions packages/shared/flows.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7534,6 +7534,7 @@ integrations:
endpoint:
method: GET
path: /users
group: Users
sync_type: incremental
scopes:
- okta.users.read
Expand All @@ -7543,10 +7544,12 @@ integrations:
output: User
endpoint:
method: POST
path: /user
path: /users
group: Users
input: OktaCreateUser
scopes:
- okta.users.manage
version: 1.0.0
add-group:
description: Adds a new group with the OKTA_GROUP type to your org
output: Group
Expand All @@ -7561,7 +7564,8 @@ integrations:
output: SuccessResponse
endpoint:
method: PUT
path: /user-group
path: /user-groups
group: User Groups
input: OktaAssignRemoveUserGroup
scopes:
- okta.groups.manage
Expand All @@ -7570,7 +7574,8 @@ integrations:
output: SuccessResponse
endpoint:
method: DELETE
path: /user-group
path: /user-groups
group: User Groups
input: OktaAssignRemoveUserGroup
scopes:
- okta.groups.manage
Expand Down
8 changes: 4 additions & 4 deletions packages/shared/lib/clients/oauth2.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ export function getSimpleOAuth2ClientConfig(
connectionConfig: Record<string, string>
): Merge<ModuleOptions, { http: WreckHttpOptions }> {
const templateTokenUrl = typeof provider.token_url === 'string' ? provider.token_url : (provider.token_url!['OAUTH2'] as string);
const tokenUrl = makeUrl(templateTokenUrl, connectionConfig, provider.token_url_encode);
const authorizeUrl = makeUrl(provider.authorization_url!, connectionConfig, provider.authorization_url_encode);
const tokenUrl = makeUrl(templateTokenUrl, connectionConfig, provider.token_url_skip_encode);
const authorizeUrl = makeUrl(provider.authorization_url!, connectionConfig, provider.authorization_url_skip_encode);

const headers = { 'User-Agent': 'Nango' };

Expand Down Expand Up @@ -157,9 +157,9 @@ export async function getFreshOAuth2Credentials(
}
}

function makeUrl(template: string, config: Record<string, any>, encodeAllParams = true): URL {
function makeUrl(template: string, config: Record<string, any>, skipEncodeKeys: string[] = []): URL {
const cleanTemplate = template.replace(/connectionConfig\./g, '');
const encodedParams = encodeAllParams ? encodeParameters(config) : config;
const encodedParams = skipEncodeKeys.includes('base_url') ? config : encodeParameters(config);
const interpolatedUrl = interpolateString(cleanTemplate, encodedParams);
return new URL(interpolatedUrl);
}
5 changes: 4 additions & 1 deletion packages/shared/providers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2591,7 +2591,8 @@ github-app-oauth:
alias: github
auth_mode: CUSTOM
authorization_url: ${connectionConfig.appPublicLink}/installations/new
authorization_url_encode: false
authorization_url_skip_encode:
- base_url
token_url:
OAUTH2: https://github.com/login/oauth/access_token
APP: https://api.github.com/app/installations/${connectionConfig.installation_id}/access_tokens
Expand Down Expand Up @@ -4469,6 +4470,8 @@ pennylane:
grant_type: authorization_code
refresh_params:
grant_type: refresh_token
authorization_url_skip_encode:
- scopes
docs: https://docs.nango.dev/integrations/all/pennylane

peopledatalabs:
Expand Down
4 changes: 2 additions & 2 deletions packages/types/lib/providers/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,13 @@ export interface BaseProvider {
};
};
authorization_url?: string;
authorization_url_encode?: boolean;
authorization_url_skip_encode?: string[];
access_token_url?: string;
authorization_params?: Record<string, string>;
scope_separator?: string;
default_scopes?: string[];
token_url?: string | TokenUrlObject;
token_url_encode?: boolean;
token_url_skip_encode?: string[];
token_params?: Record<string, string>;
authorization_url_replacements?: Record<string, string>;
redirect_uri_metadata?: string[];
Expand Down
4 changes: 4 additions & 0 deletions packages/webapp/src/components/ErrorComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import PageNotFound from '../pages/PageNotFound';
import DashboardLayout from '../layout/DashboardLayout';
import { Info } from './Info';
import type { LeftNavBarItems } from './LeftNavBar';
import { Helmet } from 'react-helmet';

export const ErrorPageComponent: React.FC<{ title: string; error: ApiError<string>; page: LeftNavBarItems }> = ({ title, error, page }) => {
if (error.error.code === 'not_found') {
Expand All @@ -11,6 +12,9 @@ export const ErrorPageComponent: React.FC<{ title: string; error: ApiError<strin

return (
<DashboardLayout selectedItem={page}>
<Helmet>
<title>Error - Nango</title>
</Helmet>
<h2 className="text-3xl font-semibold text-white mb-16">{title}</h2>
<Info variant={'destructive'}>
An error occurred, refresh your page or reach out to the support.{' '}
Expand Down
4 changes: 4 additions & 0 deletions packages/webapp/src/pages/Account/ForgotPassword.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useRequestPasswordResetAPI } from '../../utils/api';
import DefaultLayout from '../../layout/DefaultLayout';
import { Input } from '../../components/ui/input/Input';
import Button from '../../components/ui/button/Button';
import { Helmet } from 'react-helmet';

export default function Signin() {
const requestPasswordResetAPI = useRequestPasswordResetAPI();
Expand Down Expand Up @@ -34,6 +35,9 @@ export default function Signin() {

return (
<DefaultLayout>
<Helmet>
<title>Forgot Password - Nango</title>
</Helmet>
<div className="flex flex-col justify-center">
<div className="w-80 flex flex-col gap-6">
<h2 className="mt-4 text-center text-[20px] text-white">Request password reset</h2>
Expand Down
4 changes: 4 additions & 0 deletions packages/webapp/src/pages/Account/ResetPassword.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useResetPasswordAPI } from '../../utils/api';
import DefaultLayout from '../../layout/DefaultLayout';
import { Password } from './components/Password';
import Button from '../../components/ui/button/Button';
import { Helmet } from 'react-helmet';

export default function ResetPassword() {
const resetPasswordAPI = useResetPasswordAPI();
Expand Down Expand Up @@ -40,6 +41,9 @@ export default function ResetPassword() {

return (
<DefaultLayout>
<Helmet>
<title>Reset Password - Nango</title>
</Helmet>
<div className="flex flex-col justify-center">
<div className="w-80 flex flex-col gap-4">
<h2 className="text-center text-[20px] text-white">Reset password</h2>
Expand Down
174 changes: 88 additions & 86 deletions packages/webapp/src/pages/Account/Signin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import DefaultLayout from '../../layout/DefaultLayout';
import GoogleButton from '../../components/ui/button/Auth/Google';
import Button from '../../components/ui/button/Button';
import { globalEnv } from '../../utils/env';
import { Helmet } from 'react-helmet';

export default function Signin() {
const [serverErrorMessage, setServerErrorMessage] = useState('');
Expand Down Expand Up @@ -67,104 +68,105 @@ export default function Signin() {
};

return (
<>
<DefaultLayout>
<div className="flex flex-col justify-center">
<div className="flex flex-col justify-center w-80 mx-4">
<h2 className="mt-4 text-center text-[20px] text-white">Log in to Nango</h2>
<form className="mt-8 space-y-6" onSubmit={handleSubmit}>
<div>
<div className="mt-1">
<input
id="email"
placeholder="Email"
name="email"
type="email"
autoComplete="email"
required
className="border-border-gray bg-dark-600 placeholder-dark-500 text-text-light-gray block h-11 w-full appearance-none rounded-md border px-3 py-2 text-[14px] placeholder-gray-400 shadow-sm focus:outline-none"
/>
</div>
<DefaultLayout>
<Helmet>
<title>Login - Nango</title>
</Helmet>
<div className="flex flex-col justify-center">
<div className="flex flex-col justify-center w-80 mx-4">
<h2 className="mt-4 text-center text-[20px] text-white">Log in to Nango</h2>
<form className="mt-8 space-y-6" onSubmit={handleSubmit}>
<div>
<div className="mt-1">
<input
id="email"
placeholder="Email"
name="email"
type="email"
autoComplete="email"
required
className="border-border-gray bg-dark-600 text-text-light-gray block h-11 w-full appearance-none rounded-md border px-3 py-2 text-[14px] placeholder-gray-400 shadow-sm focus:outline-none"
/>
</div>
</div>

<div>
<div className="flex justify-end">
<div className="flex flex-end text-sm">
<a href="/forgot-password" className="text-dark-500 text-xs ml-1">
Forgot your password?
</a>
</div>
</div>
<div className="mt-2">
<input
id="password"
name="password"
type="password"
placeholder="Password"
autoComplete="current-password"
required
className="border-border-gray bg-dark-600 placeholder-dark-500 text-text-light-gray block h-11 w-full appearance-none rounded-md border px-3 py-2 text-[14px] placeholder-gray-400 shadow-sm focus:outline-none"
/>
<div>
<div className="flex justify-end">
<div className="flex flex-end text-sm">
<Link to="/forgot-password" className="text-dark-500 text-xs ml-1">
Forgot your password?
</Link>
</div>
</div>

<div className="grid">
<button
type="submit"
className="bg-white mt-4 flex h-11 justify-center rounded-md border px-4 pt-3 text-[14px] text-black shadow hover:border-2 active:ring-2 active:ring-offset-2"
>
Log in
</button>
{serverErrorMessage && (
<>
<p className="mt-6 place-self-center text-sm text-red-600">{serverErrorMessage}</p>
{showResendEmail && (
<Button onClick={resendVerificationEmail} className="flex justify-center mt-2 text-light-gray" variant="danger">
Resend verification email
</Button>
)}
</>
)}
<div className="mt-2">
<input
id="password"
name="password"
type="password"
placeholder="Password"
autoComplete="current-password"
required
className="border-border-gray bg-dark-600 text-text-light-gray block h-11 w-full appearance-none rounded-md border px-3 py-2 text-[14px] placeholder-gray-400 shadow-sm focus:outline-none"
/>
</div>
</div>

{globalEnv.features.managedAuth && (
<div className="grid">
<button
type="submit"
className="bg-white mt-4 flex h-11 justify-center rounded-md border px-4 pt-3 text-[14px] text-black shadow hover:border-2 active:ring-2 active:ring-offset-2"
>
Log in
</button>
{serverErrorMessage && (
<>
<div className="flex items-center justify-center my-4 text-xs">
<div className="border-t border-gray-600 flex-grow mr-7"></div>
<span className="text-dark-500">or continue with</span>
<div className="border-t border-gray-600 flex-grow ml-7"></div>
</div>

<GoogleButton text="Sign in with Google" setServerErrorMessage={setServerErrorMessage} />
<p className="mt-6 place-self-center text-sm text-red-600">{serverErrorMessage}</p>
{showResendEmail && (
<Button onClick={resendVerificationEmail} className="flex justify-center mt-2 text-light-gray" variant="danger">
Resend verification email
</Button>
)}
</>
)}
</form>
</div>
<div className="grid text-xs">
<div className="mt-7 flex place-self-center">
<p className="text-dark-500">Don&apos;t have an account?</p>
<Link to="/signup" className="text-white ml-1">
Sign up.
</Link>
</div>

{globalEnv.features.managedAuth && (
<>
<div className="flex items-center justify-center my-4 text-xs">
<div className="border-t border-gray-600 flex-grow mr-7"></div>
<span className="text-dark-500">or continue with</span>
<div className="border-t border-gray-600 flex-grow ml-7"></div>
</div>

<GoogleButton text="Sign in with Google" setServerErrorMessage={setServerErrorMessage} />
</>
)}
</form>
</div>
<div className="grid text-xs">
<div className="mt-7 flex place-self-center">
<p className="text-dark-500">Don&apos;t have an account?</p>
<Link to="/signup" className="text-white ml-1">
Sign up.
</Link>
</div>
<div className="grid w-full">
<div className="mt-8 flex text-xs">
<p className="text-dark-500">
By signing in, you agree to our
<a href="https://www.nango.dev/terms" target="_blank" rel="noreferrer" className="text-white ml-1">
Terms of Service
</a>
<span className="text-dark-500 ml-1">and</span>
<a href="https://www.nango.dev/privacy-policy" target="_blank" rel="noreferrer" className="text-white ml-1">
Privacy Policy
</a>
<span className="text-dark-500">.</span>
</p>
</div>
</div>
<div className="grid w-full">
<div className="mt-8 flex text-xs">
<p className="text-dark-500">
By signing in, you agree to our
<a href="https://www.nango.dev/terms" target="_blank" rel="noreferrer" className="text-white ml-1">
Terms of Service
</a>
<span className="text-dark-500 ml-1">and</span>
<a href="https://www.nango.dev/privacy-policy" target="_blank" rel="noreferrer" className="text-white ml-1">
Privacy Policy
</a>
<span className="text-dark-500">.</span>
</p>
</div>
</div>
</DefaultLayout>
</>
</div>
</DefaultLayout>
);
}
4 changes: 4 additions & 0 deletions packages/webapp/src/pages/Account/Signup.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { Helmet } from 'react-helmet';
import DefaultLayout from '../../layout/DefaultLayout';
import { SignupForm } from './components/SignupForm';

export const Signup: React.FC = () => {
return (
<DefaultLayout>
<Helmet>
<title>Signup - Nango</title>
</Helmet>
<div className="flex flex-col justify-center">
<div className="w-80">
<SignupForm />
Expand Down
Loading

0 comments on commit 2e0c759

Please sign in to comment.