Skip to content

Commit 01cbf45

Browse files
committed
feat: support for public apis without auth and tests
1 parent 257d30b commit 01cbf45

File tree

6 files changed

+101
-12
lines changed

6 files changed

+101
-12
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ yarn-debug.log*
66
yarn-error.log*
77
lerna-debug.log*
88

9+
# config files
10+
src/.app.json
11+
src/app.json
12+
913
# IDE exclusions
1014
.idea
1115

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
```shell
44
# do this to start server
5-
export export APP_CONFIG=./src/.app.json
5+
cp ./src/a.json ./src/.app.json
6+
export APP_CONFIG=./src/.app.json
67
npm install
78
npm run serve
89

src/auth/auth.js

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import {isString} from "@aicore/libcommonutils";
22

33
let key = null;
4+
const customAuthAPIPath = {},
5+
API_AUTH_NONE = 1;
6+
// API_AUTH_CUSTOM = 2; maybe give a callback function here?
47

58
export function init(authKey) {
69
if (!isString(authKey)) {
@@ -9,10 +12,7 @@ export function init(authKey) {
912
key = authKey;
1013
}
1114

12-
export function isAuthenticated(request) {
13-
if (!request.headers) {
14-
return false;
15-
}
15+
function _isBasicAuthPass(request) {
1616
const authHeader = request.headers.authorization;
1717
console.log(authHeader);
1818
if (!authHeader) {
@@ -31,6 +31,38 @@ export function isAuthenticated(request) {
3131
return false;
3232
}
3333

34+
/**
35+
* path of form '/x/y#df?a=30' to '/x/y' or 'https://d/x/y#df?a=30' to 'https://d/x/y'
36+
* @param url any url
37+
* @return {string} url or path without any query string or # params
38+
* @private
39+
*/
40+
function _getBaseURL(url = "") {
41+
return url.split("?")[0].split("#")[0];
42+
}
43+
44+
export function isAuthenticated(request) {
45+
let customAuth = customAuthAPIPath[_getBaseURL(request.raw.url)] || {};
46+
if( customAuth.authType === API_AUTH_NONE){
47+
return true;
48+
}
49+
if (!request.headers) {
50+
return false;
51+
}
52+
return _isBasicAuthPass(request);
53+
}
54+
55+
/**
56+
* There would be certain APIs that you need to work without auth. This function sets a given API path
57+
* as requiring no authentication.
58+
* @param {string} apiPath of the form "/path/to/api" , The route must exactly match the api name in `server.get` call.
59+
*/
60+
export function addUnAuthenticatedAPI(apiPath) {
61+
customAuthAPIPath[apiPath] = {
62+
authType: API_AUTH_NONE
63+
};
64+
}
65+
3466
export function getAuthKey() {
3567
return key;
3668
}

src/server.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
*/
1818

1919
import fastify from "fastify";
20-
import {init, isAuthenticated} from "./auth/auth.js";
20+
import {init, isAuthenticated, addUnAuthenticatedAPI} from "./auth/auth.js";
2121
import {HTTP_STATUS_CODES} from "@aicore/libcommonutils";
2222
import {getConfigs} from "./utils/configs.js";
2323
import {getHelloSchema, hello} from "./api/hello.js";
@@ -34,10 +34,17 @@ server.addHook('onRequest', (request, reply, done) => {
3434
}
3535
});
3636

37+
// public hello api
38+
addUnAuthenticatedAPI('/hello');
3739
server.get('/hello', getHelloSchema(), function (request, reply) {
3840
return hello(request, reply);
3941
});
4042

43+
// An authenticated version of the hello api
44+
server.get('/helloAuth', getHelloSchema(), function (request, reply) {
45+
return hello(request, reply);
46+
});
47+
4148
/**
4249
* It starts the server and listens on the port specified in the configs
4350
*/

test/integration/hello.spec.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,22 @@ describe('Integration Tests for hello api', function () {
1515
await close();
1616
});
1717

18-
it('should say hello', async function () {
19-
let output = await fetch("http://localhost:5000/hello?name=world", { method: 'GET', headers: {
18+
it('should say hello without auth', async function () {
19+
let output = await fetch("http://localhost:5000/hello?name=world", { method: 'GET'});
20+
output = await output.json();
21+
expect(output).eql({message: "hello world"});
22+
});
23+
24+
it('should say helloAuth if authorised', async function () {
25+
let output = await fetch("http://localhost:5000/helloAuth?name=world", { method: 'GET', headers: {
2026
authorization: "Basic hehe"
2127
}});
2228
output = await output.json();
2329
expect(output).eql({message: "hello world"});
2430
});
2531

26-
it('should not say hello if unauthorised', async function () {
27-
let output = await fetch("http://localhost:5000/hello?name=world", { method: 'GET'});
32+
it('should not say helloAuth if unauthorised', async function () {
33+
let output = await fetch("http://localhost:5000/helloAuth?name=world", { method: 'GET'});
2834
output = await output.json();
2935
expect(output).eql({
3036
"error": "Unauthorized",

test/unit/auth/auth-test.spec.js

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {isAuthenticated, init, getAuthKey} from "../../../src/auth/auth.js";
1+
import {isAuthenticated, init, getAuthKey, addUnAuthenticatedAPI} from "../../../src/auth/auth.js";
22
/*global describe, it*/
33

44
import * as chai from 'chai';
@@ -30,21 +30,27 @@ describe('unit tests for auth module', function () {
3030
const authenticated = isAuthenticated({
3131
headers: {
3232
authorization: 'Basic 1'
33+
}, raw: {
34+
url: ""
3335
}
3436

3537
}, {});
3638
expect(authenticated).eql(true);
3739
});
3840
it('isAuthenticated should fail if headers are missing', function () {
3941
init(getConfigs().authKey);
40-
const authenticated = isAuthenticated({}, {});
42+
const authenticated = isAuthenticated({raw: {
43+
url: ""
44+
}}, {});
4145
expect(authenticated).eql(false);
4246
});
4347
it('isAuthenticated should fail', function () {
4448
init('1');
4549
const authenticated = isAuthenticated({
4650
headers: {
4751
authorization: 'Basic 10'
52+
}, raw: {
53+
url: ""
4854
}
4955

5056
}, {});
@@ -56,6 +62,8 @@ describe('unit tests for auth module', function () {
5662
const authenticated = isAuthenticated({
5763
headers: {
5864
authorization: 'Basic 1 1234'
65+
}, raw: {
66+
url: ""
5967
}
6068

6169
}, {});
@@ -66,6 +74,8 @@ describe('unit tests for auth module', function () {
6674
const authenticated = isAuthenticated({
6775
headers: {
6876
authorization: '123 1'
77+
}, raw: {
78+
url: ""
6979
}
7080

7181
}, {});
@@ -76,6 +86,35 @@ describe('unit tests for auth module', function () {
7686
const authenticated = isAuthenticated({
7787
headers: {
7888
abc: '123'
89+
}, raw: {
90+
url: ""
91+
}
92+
93+
}, {});
94+
expect(authenticated).eql(false);
95+
});
96+
97+
it('addUnAuthenticatedAPI should disable authentication for given api', function () {
98+
init(getConfigs().authKey);
99+
addUnAuthenticatedAPI("/testAPI01");
100+
const authenticated = isAuthenticated({
101+
headers: {
102+
abc: '123'
103+
}, raw: {
104+
url: "/testAPI01#43?z=34"
105+
}
106+
107+
}, {});
108+
expect(authenticated).eql(true);
109+
});
110+
111+
it('addUnAuthenticatedAPI should not disable authentication if api signature mismatch with /', function () {
112+
addUnAuthenticatedAPI("/testAPI01");
113+
const authenticated = isAuthenticated({
114+
headers: {
115+
abc: '123'
116+
}, raw: {
117+
url: "/testAPI01/#43?z=34" // note the / at the end of url
79118
}
80119

81120
}, {});

0 commit comments

Comments
 (0)