Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

feat: add package for getting the full URL from a request #16

Merged
merged 1 commit into from
Jul 21, 2020
Merged
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
85 changes: 85 additions & 0 deletions docs/request-url.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
title: Get Request URL
---

A small utility to get the original URL that a request was made with. e.g. if your user types `http://example.com/foo/bar` into the address bar, and it loads the web page on your server, this should return `http://example.com/foo/bar`.

## Installation

To install, run the following command in your terminal:

```
yarn add @authentication/request-url
```

## Usage

Calling `getRequestURL` with either an express request or koa context returns the `URL` object representing the full URL that the visitor navigated to in order to load the page.

```typescript
import getRequestURL from '@authentication/cloudflare-ip';
import express from 'express';

const app = express();

app.use((req, res) => {
res.send(`The URL you requested is: ${getRequestURL(req).href}`);
});

app.listen(process.env.PORT || 3000);
```

```javascript
const getRequestURL = require('@authentication/cloudflare-ip');
const express = require('express');

const app = express();

app.use((req, res) => {
res.send(`The URL you requested is: ${getRequestURL(req).href}`);
});

app.listen(process.env.PORT || 3000);
```

### Trust Proxy

If you run your app behind a proxy in production, you will also need to pass `{trustProxy: true}`. `trustProxy` is enabled by default if `NODE_ENV=development` to make it easier to use `getRequestURL` with setups like webpack-dev-server.

**N.B.** If a malicious user finds a way to bypass the proxy, or if the proxy does not overwrite the `x-forwarded-host` and `x-forwarded-proto` headers, it may be possible for a malicous attacker to force this function to return a URL for a server you do not control.

### Base URL

Instead of setting `trustProxy`, if you know the host name you expect your app to be running on, you should set the `baseURL` option. This is much more secure. You can use an environment variable to support different values between development, staging and production:

```typescript
import getRequestURL from '@authentication/cloudflare-ip';
import express from 'express';

const app = express();

app.use((req, res) => {
res.send(`The URL you requested is: ${getRequestURL(req, {
// set this variable to something like: https://www.example.com
baseURL: process.env.BASE_URL,
}).href}`);
});

app.listen(process.env.PORT || 3000);
```

```javascript
const getRequestURL = require('@authentication/cloudflare-ip');
const express = require('express');

const app = express();

app.use((req, res) => {
res.send(`The URL you requested is: ${getRequestURL(req, {
// set this variable to something like: https://www.example.com
baseURL: process.env.BASE_URL,
}).href}`);
});

app.listen(process.env.PORT || 3000);
```
13 changes: 10 additions & 3 deletions packages/oauth1/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Cookie from '@authentication/cookie';
import {Mixed} from '@authentication/types';
import AuthorizationError from './errors/AuthorizationError';
import StateVerificationFailure from './errors/StateVerificationFailure';
import originalURL from './originalURL';
import getRequestURL from '@authentication/request-url';
const OAuth1Base = require('oauth').OAuth;

function parseURL(name: string, input: URL | string, base?: string | URL) {
Expand Down Expand Up @@ -239,7 +239,14 @@ export default class OAuth1Authentication<State = Mixed> {
typeof callbackURLInitial === 'string'
? new URL(
callbackURLInitial,
originalURL(req, {trustProxy: this._trustProxy}),
getRequestURL(req, {
trustProxy:
this._trustProxy === undefined
? req.app.get('trust proxy') ||
process.env.NODE_ENV === 'development'
: this._trustProxy,
baseURL: process.env.BASE_URL || process.env.BASE_URI,
}),
)
: callbackURLInitial;
if (callbackURL) {
Expand All @@ -261,7 +268,7 @@ export default class OAuth1Authentication<State = Mixed> {
}
const userAuthorizationParams = options.userAuthorizationParams;
if (userAuthorizationParams) {
Object.keys(userAuthorizationParams).forEach(key => {
Object.keys(userAuthorizationParams).forEach((key) => {
userAuthorizationURL.searchParams.set(
key,
userAuthorizationParams[key],
Expand Down
36 changes: 0 additions & 36 deletions packages/oauth1/src/originalURL.ts

This file was deleted.

13 changes: 10 additions & 3 deletions packages/oauth2/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import StateVerificationFailure from './errors/StateVerificationFailure';
import TokenError from './errors/TokenError';
import InternalOAuthError from './errors/InternalOAuthError';
import getUID from './getUID';
import originalURL from './originalURL';
import getRequestURL from '@authentication/request-url';
const OAuth2Base = require('oauth').OAuth2;

// This type used to be exported from http but has gone missing
Expand Down Expand Up @@ -261,7 +261,14 @@ export default class OAuth2Authentication<State = Mixed, Results = Mixed> {
return typeof this._callbackURL === 'string'
? new URL(
this._callbackURL,
originalURL(req, {trustProxy: this._trustProxy}),
getRequestURL(req, {
trustProxy:
this._trustProxy === undefined
? req.app.get('trust proxy') ||
process.env.NODE_ENV === 'development'
: this._trustProxy,
baseURL: process.env.BASE_URL || process.env.BASE_URI,
}),
)
: this._callbackURL;
}
Expand All @@ -279,7 +286,7 @@ export default class OAuth2Authentication<State = Mixed, Results = Mixed> {
const authorizeUrl = new URL(this._authorizeURL.href);
const p = options.params;
if (p) {
Object.keys(p).forEach(key => {
Object.keys(p).forEach((key) => {
authorizeUrl.searchParams.set(key, p[key]);
});
}
Expand Down
39 changes: 0 additions & 39 deletions packages/oauth2/src/originalURL.ts

This file was deleted.

29 changes: 18 additions & 11 deletions packages/passwordless/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import Store, {
StoreTransaction,
} from './Store';
import Token from './Token';
import originalURL from './originalURL';
import getRequestURL from '@authentication/request-url';

import {
CreateTokenStatusKind,
Expand Down Expand Up @@ -173,8 +173,8 @@ export default class PasswordlessAuthentication<State> {
options.maxAge === undefined
? ms('1 hour')
: typeof options.maxAge === 'number'
? options.maxAge
: ms(options.maxAge);
? options.maxAge
: ms(options.maxAge);
if (
typeof this._maxAge !== 'number' ||
isNaN(this._maxAge) ||
Expand Down Expand Up @@ -210,7 +210,7 @@ export default class PasswordlessAuthentication<State> {
// from behind a single router, but over the long run, we want to keep
// this pretty low or someone could be quite abusive.
this._createTokenByIpRateLimit = new BucketRateLimit(
this._getStore(ip => 'create_ip_' + ip),
this._getStore((ip) => 'create_ip_' + ip),
{
interval: '10 minutes',
maxSize: 20,
Expand All @@ -223,7 +223,7 @@ export default class PasswordlessAuthentication<State> {
// a few attempts. It is possible a user might get spammed with a
// few token e-mails, but this will quickly stem the tide.
this._createTokenByUserRateLimit = new ExponentialRateLimit(
this._getStore(userID => 'user_' + userID),
this._getStore((userID) => 'user_' + userID),
{
baseDelay: '5 minutes',
factor: 2,
Expand All @@ -235,7 +235,7 @@ export default class PasswordlessAuthentication<State> {
// We don't use an exponential backoff because resetting it when a
// correct token attempt happens would defeat the point.
this._validatePassCodeByIpRateLimit = new BucketRateLimit(
this._getStore(ip => 'validate_ip_' + ip),
this._getStore((ip) => 'validate_ip_' + ip),
{
interval: '10 minutes',
maxSize: 20,
Expand All @@ -253,7 +253,14 @@ export default class PasswordlessAuthentication<State> {
return typeof this._callbackURL === 'string'
? new URL(
this._callbackURL,
originalURL(req, {trustProxy: this._trustProxy}),
getRequestURL(req, {
trustProxy:
this._trustProxy === undefined
? req.app.get('trust proxy') ||
process.env.NODE_ENV === 'development'
: this._trustProxy,
baseURL: process.env.BASE_URL || process.env.BASE_URI,
}),
)
: new URL(this._callbackURL.href);
}
Expand All @@ -265,8 +272,8 @@ export default class PasswordlessAuthentication<State> {
}
private _getStore<T>(idToString: (id: T) => string): RateLimitStoreAPI<T> {
return {
tx: fn =>
this._store.tx(store =>
tx: (fn) =>
this._store.tx((store) =>
fn({
save(id, state, oldState) {
return store.saveRateLimit(idToString(id), state, oldState);
Expand Down Expand Up @@ -323,7 +330,7 @@ export default class PasswordlessAuthentication<State> {
]);
const passCodeHash = await hash(passCode);
// store the token
const tokenID = await this._store.tx(store =>
const tokenID = await this._store.tx((store) =>
store.saveToken({
userID,
dos: dosCode,
Expand Down Expand Up @@ -446,7 +453,7 @@ export default class PasswordlessAuthentication<State> {
(await verify(
passCode,
token.passCodeHash,
async updatedPassCodeHash => {
async (updatedPassCodeHash) => {
// we're about to delete the token anyway,
// so no need to update it
},
Expand Down
39 changes: 0 additions & 39 deletions packages/passwordless/src/originalURL.ts

This file was deleted.

3 changes: 0 additions & 3 deletions packages/passwordless/types.d.ts

This file was deleted.

3 changes: 0 additions & 3 deletions packages/passwordless/types.js

This file was deleted.

3 changes: 3 additions & 0 deletions packages/request-url/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @authentication/request-url

For documentation, see https://www.atauthentication.com/docs/request-url.html
16 changes: 16 additions & 0 deletions packages/request-url/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "@authentication/request-url",
"version": "0.0.0",
"description": "",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"dependencies": {},
"scripts": {},
"repository": "https://github.com/ForbesLindesay/authentication/tree/master/packages/request-url",
"bugs": "https://github.com/ForbesLindesay/authentication/issues",
"license": "GPL-3.0",
"publishConfig": {
"access": "public"
},
"homepage": "https://www.atauthentication.com/docs/request-url.html"
}
Loading