Skip to content

Feat/middleware #36

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

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
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
11 changes: 0 additions & 11 deletions .babelrc

This file was deleted.

60 changes: 31 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
## How it works

This library adds a password prompt to your Next.js deployment. It consists of two main parts:

1. Two serverless API routes:
- A login route that checks if a password is correct and sets a cookie with a JWT in case it is.
- A check route that validates if you have the authorization cookie with a valid JWT.
Expand Down Expand Up @@ -93,9 +94,9 @@ import { withPasswordProtect } from "@storyofams/next-password-protect";
// Before: export default App;
export default process.env.PASSWORD_PROTECT
? withPasswordProtect(App, {
// Options go here (optional)
loginApiUrl: "/login",
})
// Options go here (optional)
loginApiUrl: "/login",
})
: App;
```

Expand All @@ -104,53 +105,54 @@ export default process.env.PASSWORD_PROTECT
## API

### API routes handlers
```loginHandler(password: string, options)```

`loginHandler(password: string, options)`

The options object can contain any of the following options:

Option | Description | Default value
------ | ----------- | -------------
`cookieMaxAge`| Cookie Max-Age attribute | `undefined`
`cookieName`| The name of the authorization cookie | `'next-password-protect'`
`cookieSameSite`| SameSite cookie attribute | `false`
`cookieSecure`| Secure flag on the cookie | `process.env.NODE_ENV === 'production'`
| Option | Description | Default value |
| ---------------- | ------------------------------------ | --------------------------------------- |
| `cookieMaxAge` | Cookie Max-Age attribute | `undefined` |
| `cookieName` | The name of the authorization cookie | `'next-password-protect'` |
| `cookieSameSite` | SameSite cookie attribute | `false` |
| `cookieSecure` | Secure flag on the cookie | `process.env.NODE_ENV === 'production'` |

```passwordCheckHandler(password: string, options)```
`passwordCheckHandler(password: string, options)`

The options object can contain any of the following options:

Option | Description | Default value
------ | ----------- | -------------
`cookieName`| The name of the authorization cookie | `'next-password-protect'`

| Option | Description | Default value |
| ------------ | ------------------------------------ | ------------------------- |
| `cookieName` | The name of the authorization cookie | `'next-password-protect'` |

### Next App HOC
```withPasswordProtect(App: NextApp, options)```

`withPasswordProtect(App: NextApp, options)`

The options object can contain any of the following options:

Option | Description | Default value
------ | ----------- | -------------
`checkApiUrl`| Relative path of the api route handled by `passwordCheckHandler` | `'/api/passwordCheck'`
`loginApiUrl`| Relative path of the api route handled by `loginHandler` | `'/api/login'`
`loginComponent`| Supply your own React component to show as login prompt | `LoginComponent`
`loginComponentProps`| Properties object to customize the login prompt, without overriding the entire component (see below) | `{}`
| Option | Description | Default value |
| --------------------- | ---------------------------------------------------------------------------------------------------- | ---------------------- |
| `checkApiUrl` | Relative path of the api route handled by `passwordCheckHandler` | `'/api/passwordCheck'` |
| `loginApiUrl` | Relative path of the api route handled by `loginHandler` | `'/api/login'` |
| `loginComponent` | Supply your own React component to show as login prompt | `LoginComponent` |
| `loginComponentProps` | Properties object to customize the login prompt, without overriding the entire component (see below) | `{}` |

The `loginComponentProps` object can contain any of the following options:

Option | Description | Default value
------ | ----------- | -------------
`backUrl`| Show a link with this URL to go back to main website | `undefined`
`buttonBackgroundColor`| Login button background color | `'#01EDBC'`
`buttonColor`| Login button color | `'#111'`
`logo` | Show a logo above the prompt (img src) | `undefined`
| Option | Description | Default value |
| ----------------------- | ---------------------------------------------------- | ------------- |
| `backUrl` | Show a link with this URL to go back to main website | `undefined` |
| `buttonBackgroundColor` | Login button background color | `'#01EDBC'` |
| `buttonColor` | Login button color | `'#111'` |
| `logo` | Show a logo above the prompt (img src) | `undefined` |

## Advanced

### Custom login component

To change the default login component, a React component can be supplied to the `withPasswordProtect` HOC. In order for the library to function properly, make sure your login component has password input that is validated by the the api route.
You can use `src/hoc/LoginComponent.tsx` as a starting point.
You can use `src/components/Login.tsx` as a starting point.

## Caveats

Expand Down
12 changes: 12 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module.exports = {
presets: ['@babel/preset-env', '@babel/preset-typescript', '@babel/react'],
plugins: [
'@babel/plugin-proposal-class-properties',
'@babel/plugin-transform-runtime',
],
env: {
test: {
plugins: ['@babel/plugin-transform-modules-commonjs'],
},
},
};
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
34 changes: 34 additions & 0 deletions examples/hoc/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const path = require('path');
const { TsconfigPathsPlugin } = require('tsconfig-paths-webpack-plugin');

module.exports = {
env: {
PASSWORD_PROTECT: process.env.ENVIRONMENT === 'staging',
},
eslint: {
// Warning: This allows production builds to successfully complete even if
// your project has ESLint errors.
ignoreDuringBuilds: true,
},
experimental: {
externalDir: true,
},
webpack(config, options) {
config.module.rules.push({
test: /\.svg$/,
use: [{ loader: '@svgr/webpack', options: { icon: true, svgo: false } }],
});

config.resolve.plugins = [
new TsconfigPathsPlugin({ extensions: config.resolve.extensions }),
];

config.resolve.alias = {
...config.resolve.alias,
react: path.resolve('./node_modules/react'),
'react-dom': path.resolve('./node_modules/react-dom'),
};

return config;
},
};
2 changes: 1 addition & 1 deletion example/package.json → examples/hoc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"dependencies": {
"@reach/alert": "^0.7.4",
"@storyofams/react-helpers": "0.3.6",
"@storyofams/next-password-protect": "link:..",
"@storyofams/next-password-protect": "link:../..",
"@styled-system/css": "^5.1.4",
"@styled-system/props": "^5.1.4",
"@svgr/webpack": "^5.0.1",
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { seo } from '~/config';
import theme from '~/styles/theme';

import '../../public/static/fonts/stylesheet.css';
import { withPasswordProtect } from '../../../src';
import { withPasswordProtect } from '../../../../src';

class MyApp extends App {
componentDidMount() {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
9 changes: 3 additions & 6 deletions example/yarn.lock → examples/hoc/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2154,12 +2154,9 @@
telejson "^3.2.0"
util-deprecate "^1.0.2"

"@storyofams/next-password-protect@link:..":
version "0.0.0-development"
dependencies:
cookie "^0.4.1"
jsonwebtoken "^8.5.1"
safe-compare "^1.1.4"
"@storyofams/next-password-protect@link:../..":
version "0.0.0"
uid ""

"@storyofams/[email protected]":
version "0.3.6"
Expand Down
1 change: 1 addition & 0 deletions examples/middleware/.commitlintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = { extends: ["@commitlint/config-conventional"] };
69 changes: 69 additions & 0 deletions examples/middleware/.cz-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
module.exports = {
// add additional standard scopes here
scopes: [{ name: "accounts" }, { name: "admin" }],
// use this to permanently skip any questions by listing the message key as a string
skipQuestions: [],

/* DEFAULT CONFIG */
messages: {
type: "What type of changes are you committing:",
scope: "\nEnlighten us with the scope (optional):",
customScope: "Add the scope of your liking:",
subject: "Write a short and simple description of the change:\n",
body:
'Provide a LONGER description of the change (optional). Use "|" to break new line:\n',
breaking: "List any BREAKING CHANGES (optional):\n",
footer:
"List any ISSUES CLOSED by this change (optional). E.g.: #31, #34:\n",
confirmCommit: "Are you sure you the above looks right?",
},
types: [
{
value: "fix",
name: "🐛 fix: Changes that fix a bug",
emoji: "🐛",
},
{
value: "feat",
name: " 🚀 feat: Changes that introduce a new feature",
emoji: "🚀",
},
{
value: "refactor",
name:
"🔍 refactor: Changes that neither fixes a bug nor adds a feature",
emoji: "🔍",
},
{
value: "test",
name: "💡 test: Adding missing tests",
emoji: "💡",
},
{
value: "style",
name:
"💅 style: Changes that do not impact the code base \n (white-space, formatting, missing semi-colons, etc)",
emoji: "💅",
},
{
value: "docs",
name: "📝 docs: Changes to the docs",
emoji: "📝",
},
{
value: "chore",
name:
"🤖 chore: Changes to the build process or auxiliary tools\n and or libraries such as auto doc generation",
emoji: "🤖",
},
],
allowTicketNumber: false,
isTicketNumberRequired: false,
ticketNumberPrefix: "#",
ticketNumberRegExp: "\\d{1,5}",
allowCustomScopes: true,
allowBreakingChanges: ["feat", "fix", "chore"],
breakingPrefix: "🚧 BREAKING CHANGES 🚧",
footerPrefix: "CLOSES ISSUE:",
subjectLimit: 100,
};
2 changes: 2 additions & 0 deletions examples/middleware/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ENVIRONMENT=staging
STAGING_PASSWORD=secret
7 changes: 7 additions & 0 deletions examples/middleware/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules/*
node_modules
public/*
storybook-static/*
dist
.next
.next/*
26 changes: 26 additions & 0 deletions examples/middleware/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
.env*
!.env.example

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
7 changes: 7 additions & 0 deletions examples/middleware/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules/*
public/*
.next
.next/*
dist
coverage
storybook-static/*
10 changes: 10 additions & 0 deletions examples/middleware/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<p align="center">
<a href="https://storyofams.com/" target="_blank" align="center">
<img src="https://storyofams.com/public/[email protected]" alt="Story of AMS" width="120">
</a>
<h1 align="center">@storyofams/next-password-protect example</h1>
</p>

## Setup

Rename `.env.example` to `.env.local`
19 changes: 19 additions & 0 deletions examples/middleware/custom.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { SxStyleProp } from 'rebass';
import * as StyledComponents from 'styled-components';
import * as StyledSystem from 'styled-system';

declare module 'rebass' {
type ThemedSxStyleProps =
| SxStyleProp
| StyledSystem.SpaceProps<StyledComponents.DefaultTheme>
| StyledSystem.TypographyProps<StyledComponents.DefaultTheme>
| StyledSystem.FlexboxProps<StyledComponents.DefaultTheme>
| StyledSystem.GridProps<StyledComponents.DefaultTheme>
| StyledSystem.LayoutProps<StyledComponents.DefaultTheme>
| StyledSystem.ColorProps<StyledComponents.DefaultTheme>;

export interface SxProps {
maatje?: boolean;
sx?: ThemedSxStyleProps;
}
}
19 changes: 19 additions & 0 deletions examples/middleware/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module.exports = {
testEnvironment: 'jsdom',
roots: ['<rootDir>/src'],
transform: {
'^.+\\.tsx?$': 'babel-jest',
},
moduleNameMapper: {
'^~/(.*)$': '<rootDir>/src/$1',
},
moduleDirectories: [
'node_modules',
'src/lib', // a utility folder
__dirname, // the root directory
],
setupFilesAfterEnv: [
'@testing-library/jest-dom/extend-expect',
'./jest.setup.js',
],
};
1 change: 1 addition & 0 deletions examples/middleware/jest.setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
jest.mock('./src/components/common/Icon/req');
6 changes: 6 additions & 0 deletions examples/middleware/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
File renamed without changes.
Loading