Connect.Account is an open source web application, which aims at providing all the business logic to handle Connect identities, profile and addresses data, and a default frontend for users to use.
Before being able to start the project locally, you should have a way to run Node.js
applications.
We recommend you to install the latest version of asdf
to do so.
You will need nodejs
and yarn
.
These can be installed via the provided .tool-versions
using asdf install
.
Get required environment variables by copying .env.sample
and if necessary tuning it to values relevant to your dev environment:
cp .env_vars.sample .env_vars
ln -s Procfile.dev Procfile
Source the file to get variables defined your environnement:
source .env_vars
The application also needs couple external tools to work properly: - hivemind - docker - docker-compose - nginx - mkcert
To install them on MacOS, you can run the following:
brew bundle install --file Brewfile
Then, install dependencies:
yarn install
To allow TLS locally, we are using a nginx
proxy. The proxy listens port 29704, and redirects — with the certificates — on port 29703.
You might need the HTTPS certificates on your localhost to allow cookies writing.
If you are using Firefox as your web browser, you’ll need nss
alongside mkcert
.
brew install nss
To generate certificates:
mkdir certs (cd certs; mkcert connect-account.local)
Then, add the local FQDN in /etc/hosts
:
echo "127.0.0.1 connect-account.local" | sudo tee -a /etc/hosts echo "::1 connect-account.local" | sudo tee -a /etc/hosts
hivemind # or overmind s
# In an other terminal tab
docker-compose up
Access your application on URL: https://connect-account.local:29704
.
If you want to build and start the application, you should run the following:
yarn build && yarn start [-p 29703]
We made the choice to use DynamoDB.
The users
DynamoDB table is created automatically during the docker compose launch.
If you want to interact with the database, you can use the AWS CLI, of use one of the following npm scripts that use the SDK:
Name | Description |
---|---|
|
Create the |
|
Reset the |
|
Delete the |
|
Put a user entry in the |
|
Get the user entry the |
To run integration and unit tests :
yarn test
To run the e2e tests suite on local environment :
yarn test:e2e:local
We use a set of strict linting rules through TypeScript
and ESLint
. While TypeScript
config is pretty standard, the ESLint
one is mostly set with our own custom package, called @fewlines/eslint-config. You should read the documentation if you want the full power of the config while using VSCode
.
Note that, contrary to
errors
,warnings
do not break testing or app compilation.
You can manually lint, using:
yarn lint
or
yarn lint --fix
if you want to automatically fix linting issues.
Name | Description |
---|---|
|
Should be set to |
|
Local port used to run the application. |
|
The password used to seal or access the cookie session. It needs to be at least 32 characters long. |
|
Hostname of the account web application. This is not needed on |
|
Region of the AWS cluster. |
|
URL of the AWS cluster where your DynamoDB instance run from. |
|
Access key ID used for production when your DB is hosted by AWS. |
|
Secret access key used for production when your DB is hosted by AWS. |
|
Name of the DynamoDB table. You can see this as the name of the DB, as tables are different from relational DB in the context of a DynamoDB. |
|
URL used to fetch identities from the management GraphQL endpoint. |
|
API key used to access the management GraphQL endpoint. |
|
URL used to start the connect oauth flow. |
|
Client ID of the online service (e.g. internet website, application) that uses the Provider Authentication and Authorization service for its User. |
|
Paired with the client ID, used to authenticate the Application from which the User intent to sign in. |
|
Represents the kind of user authorized information and actions that an Application is able to access on another Application. |
|
URL used for the |
|
URL used for the Connect authentication flow. |
|
Name of the Application that identifies the recipients that the JWT is intended for. |
|
Represents the kind of user authorized information and actions that an Application is able to access on another Application. |
|
The PEM formatted private key used to decrypt the JWE access token. (i.e. "-----BEGIN RSA PRIVATE KEY-----\nqewnjfb…\n..") |
|
A boolean value that indicates if the JWE access token is signed or not. |
|
Service name for Lightstep. |
|
Your Lightstep access token. |
|
Config of the collectors used for tracing purposes. |
|
Auth token required to contact Sentry. |
|
Should be set to |
|
Password used to initiate the Memcached Client for rate limiting. |
|
Username used to initiate the Memcached Client for rate limiting. |
|
Address of the Memcached Client servers. |
|
Email of the Connect account that will be used for e2e tests. |
|
Password of the Connect account that will be used for e2e tests. |
|
Sub of the Connect account that will be used for e2e tests. |
|
URL of the Connect.Account application that will be used for e2e tests. |
|
Set to true if you need to do a Lighthouse audit on |
|
URL of Connect.Profile server. |
To understand the flow of connect-account
, you should read the connect
documentation.
To understand the abstraction added by the @fewlines/connect-client
, please read the documentation.
We are using the NextJS folder architecture (i.e. /pages
) to utilize its router, out of the box. For more information, please refer to the documentation.
We are also using the Command Query Responsibility Segregation
(CQRS) pattern to separate queries from mutations. They are located in the queries/
and command/
folder.
-
workflows/: GitHub Actions used to run tests during CI/CD process flow.
-
/dependabot.yml: Config file for dependabot.
-
/PULL_REQUEST_TEMPLATE.md: Template used when opening a pull request on GitHub.
-
dynamodb/: Scripts to interact with your local DynamoDB instead of the AWS CLI, which requires sensitives admin credentials.
-
e2e/: Scripts related to e2e tests, used in our CI/CD pipeline or in local environment.
-
@types/: Type declaration used in multiple places.
-
commands/: Write (e.g.
POST
) database actions. -
components/: React functional components used to render.
-
configs/:
-
config-variables.ts: Entry point used to verify env vars sourcing, and prevent the app to run if forgotten.
-
db-client.ts: Singleton of the DynamoDB client.
-
intl.ts: File regrouping all the
intl
object, which are instances to store the cache of all Intl.* APIs, configurations, compiled messages and such. -
logger.ts: Singleton of the logger client provided by
@fwl/web
. -
oauth2-client.ts: Singleton of the OAuth2 Client provided by
@fewlines/connect-client
. -
profile-client.ts: Singleton of the Profile Client provided by Connect Profile.
-
rate-limiting-config.ts: Config used for the rate limiting middleware provided by
@fwl/web
. -
tracer.ts: Singleton of the tracer client provided by
@fwl/web
.
-
-
design-system/:
-
global-style.css: Global style CSS rules and
tailwindcss
built-in rules.
-
-
errors/:
-
errors.ts: List of exceptions related to Connect.Account.
-
web-errors.ts: List of exceptions related to
@fwl/web
WebErrors.
-
-
middlewares/: Reusable wrappers to add various features to server side actions.
-
pages/: NextJS router.
-
queries/: Read (i.e.
GET
) database actions. -
utils/: Small snippets/functions used multiple times throughout the application.
-
workflows/: workflows used in multiples places.
-
config/: Config files for the different libraries used to test, and import fix files.
-
e2e/: Centralized e2e test files.
-
mocks/: Centralized mocked data used in different test files.
-
pages/: Centralized Next.js pages integration tests.
-
unit/: Centralized components and functions unit tests.
-
.dockerignore: Ignored files for the Docker image build process.
-
.env_vars.sample: Environment variables template file. You will need to copy this file, remove the
.sample
part, and add the correct values. -
.gitignore: GitHub config file used to prevent the pushing of certain files.
-
.tool-version: asdf config file.
-
app.json: Building instructions for Heroku.
-
assets.d.ts: Type declaration allowing the import of assets in TypeScript files.
-
Brewfile: Tools needed that will be installed via Brew for MacOS users.
-
connect-profile-openapi.yml: OpenAPI file of Connect Profile.
-
docker-compose.yml: Instructions to launch DynamoDB and the observability tools.
-
Dockerfile: Instructions for Docker image build process.
-
lighthouserc.js: Instructions for Lighthouse audits.
-
next-env.d.ts: Adds NextJS types globally.
-
next.config.js: Extended webpack compiler config used by NextJS.
-
nginx.conf: Local proxy configuration.
-
otel-collector-config: Configuration of the OpenTelemetry collector, which is used to receive, process and export tracing data.
-
package.json: We use this file, as much as possible as a centralized config file for various packages, like
ESLint
,Jest
orBabel
. -
Procfile.dev: Instructions for Hivemind/Overmind.
-
README.adoc: Connect.Account documentation, written in AsciiDoc.
-
sentry.client.config.js: Client side sentry config file.
-
sentry.properties: Variables used by Sentry to connect the app with its Sentry instance.
-
sentry.server.config.js: Server side sentry config file.
-
tsconfig.json: TypeScript compiler options.
-
yarn.lock: Package manager instructions.
The rule we follow is that, if a declared type is only used in one file, we locate it in said file. Otherwise, we move it in its own file, under @types/
.
The exceptions to this rule are next-env.d.ts and assets.d.ts as NextJS required them to be located at the root of the repository.
We chose to type React component like so:
import React from "react";
// Without props.
const Foo: React.FC = () => {
return <React.Fragment />;
};
// With props.
const Bar: React.FC<{ foo: "bar" }> = ({ foo }) => {
return <div>{foo}</div>;
};
If you are not familiar with TypeScript generic types, please take a look at the documentation.
Next’s Link
component requires its child to be an anchor tag. To lighten the JSX, we made a custom component called NeutralLink
that provides the anchor tag.
When adding a new supported Social Identity to the application, remember to add the corresponding icon as SVG.
To help with repetitive tasks during the development of a feature, or to help debugging, we have added a set dev buttons to trigger various action on press. You can find them inside src/components/dev-buttons
.
To enable them, you will have to render <DevButtons/>
inside _app.tsx
, like this:
import { DevButtons } from "@src/components/dev-buttons/dev-buttons";
const AccountApp: React.FC = ({ children }) => {
return (
<SSRProvider>
<ThemeProvider theme={theme}>
<Head>
<meta
name="viewport"
content="initial-scale=1.0, width=device-width"
/>
<title>Connect Account</title>
</Head>
<GlobalStyle />
<AlertMessages />
<SWRConfig
value={{
// ...
}}
>
{children}
</SWRConfig>
<DevButtons/>
</ThemeProvider>
</SSRProvider>
);
};
We have also added a test to ensure that the component is not being rendered in review/production env, so don’t forget to remove <DevButtons/>
when you are done. If you need to add new buttons, feel free to do so.
The global-style
CSS sheet, found in /src/design-system/global-style.css
, is used to import built-in tailwincss
rules and remove undesired style and behavior found in HTML.
Note that we chose to set the global font size to 62.5%. This font size means that '1rem' is exactly equal to '10px', which makes setting REM values very simple.
If you want to use SVGs in your application, we recommend to render them as a React component, instead of importing the file:
import React from 'react'
const SVGIcon: React.FC = () => {
return (
<svg>
// ...
</svg>
)
};
export { SVGIcon };
When SVG are monochromatic (i.e. icons) and to improve components reusability, we replace the fill
attribute value of tags composing SVG, such as path
, with the CSS variable currentColor
instead of hardcoded color codes.
This way, the SVG will be rendered with the color inherited from its parent.
import React from 'react';
const CrossIcon: React.FC = () => {
return (
<svg>
<path fill="currentColor">...</path>
// ...
</svg>
)
};
When you use Figma to export SVG, be sure to export the upper component (i.e. name 40x40).
Also remember to add a <title/>
JSX tag under the <svg/>
tag for accessibility.
We are using DynamoDB as our persistence layer. Its K/V structure allows fast performances, but you’ll need to be aware of some specificities, like being unable to update an existing value.
We store the access_token
and sub
in what we call a UserCookie
. It is a sealed object living in the user’s browser.
We also store alert messages in the user cookies, with no sealing.
We want our application to be accessible. To do so, besides following good practices and standards, we chose to use @react-aria external library to ease the accessibility implementation.
We made the choice to use react-intl, and to localize our strings to English and French.
Localized strings are located inside en.ts
and fr.ts
. The strings are organized following the router path they will called on. We then give the string an ID to be able to call them.
Note that the ID naming is still WIP.
To localize client side generated strings, you can use the useIntl
hook like this:
import React from "react";
import { useIntl } from "react-intl";
const LocalizedString: React.FC = () => {
const { formatMessage } = useIntl();
return (
<p>{formatMessage({ id: "stringID" })}</p>
)
};
This will call the key’s value inside the router path’s key. If no value are found, the ID will be used as fallback.
For server side generated strings, we generate an intl
object, and pass the needed strings (see intl.ts
).
import { createIntl, createIntlCache } from "@formatjs/intl";
import * as locales from "@content/locales";
const cache = createIntlCache();
const enIntl = createIntl(
{
locale: "en",
messages: { ...locales["en"].alertMessages },
},
cache,
);
export "enIntl"
For unit testing, we are using Jest.
We are using Testing Library to test components behavior regarding user interactions.
We are using Taiko to test our workflows.
To automate our test processes, we use CircleCI, which allow us to run our test suites on new commit pushed to each pull request.
Several flows (or jobs
) are executed in this context and orchestrated in workflows inside the config.yml
file:
-
lint-and-tests: runs the code linting and the integration/unit tests.
-
e2e-tests: runs the e2e test suite on a deployed Connect.Account version.
-
lighthouse: runs the Lighthouse audits on a deployed Connect.Account version.
-
promote-to-production: used to automate the production deployment from staging when the test suite is successful.
We are using Sentry to monitor production and review app’s exceptions raised.
We’re using Sentry Next.js SDK and have added a wrapper around the withSentry
middleware to be able to use is it with SSR.
We are using PlantUML to make the sequences diagram.
To compile your PlantUML code, you can run the following:
cat name-of-the-file.uml | docker run --rm -i fewlines/developers-portal-diagram-generator plantuml -Djava.awt.headless=true -p -tjpg > name-of-the-file.jpg