Skip to content
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

refactor: update nf1 example with new notehub js oauth workflow [DO NOT MERGE] #114

Closed
wants to merge 31 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6f075b7
fixing notification store for valve-monitor
paigen11 Dec 14, 2022
c4de93f
Merge branch 'main' of github.com:blues/app-accelerators
paigen11 Dec 21, 2022
e6bae96
Merge branch 'main' of github.com:blues/app-accelerators
paigen11 Jan 4, 2023
50fa20e
Merge branch 'main' of github.com:blues/app-accelerators
paigen11 Jan 10, 2023
3dfe310
Merge branch 'main' of github.com:blues/app-accelerators
paigen11 Jan 10, 2023
d786c88
Merge branch 'main' of github.com:blues/app-accelerators
paigen11 Jan 10, 2023
3a5bd8f
Merge branch 'main' of github.com:blues/app-accelerators
paigen11 Jan 10, 2023
5bed598
Merge branch 'main' of github.com:blues/app-accelerators
paigen11 Jan 12, 2023
a41f9e2
:Merge branch 'main' of github.com:blues/app-accelerators into main
paigen11 Jan 23, 2023
4e06dfe
âMerge branch 'main' of github.com:blues/app-accelerators into main
paigen11 Feb 1, 2023
ad13406
Merge branch 'main' of github.com:blues/app-accelerators
paigen11 Feb 9, 2023
bbd02a3
Merge branch 'main' of github.com:blues/app-accelerators into main
paigen11 Feb 19, 2023
1504922
≈Merge branch 'main' of github.com:blues/app-accelerators into main
paigen11 Feb 28, 2023
86ac0a3
Merge branch 'main' of github.com:blues/app-accelerators into main
paigen11 Mar 17, 2023
671d65d
wip: testing new auth tokens in notehub-js
paigen11 Mar 20, 2023
4df617f
refactor: change code to use new oauth notehub client id and client s…
paigen11 Mar 21, 2023
16977a3
refactor: fix up notehub api calls to use updated oauth workflow
paigen11 Mar 21, 2023
4c14192
deps: bump up version of Notehub JS lib
paigen11 Mar 23, 2023
9483cf4
deps: bump yarn lock too
paigen11 Mar 23, 2023
8785efb
Update 01-indoor-floor-level-tracker/web-app/netlify.toml
paigen11 Mar 24, 2023
eb4a437
refactor: trying to add check to avoid unneccessary notehub api calls
paigen11 Mar 27, 2023
754af79
refactor: add check validity of oauth token
paigen11 Mar 29, 2023
9a7dc2b
refactor: running across all the oauth edge cases
paigen11 Mar 29, 2023
beb9b92
fix: fixing ts errors and cleaning up
paigen11 Mar 29, 2023
2063606
test: adding back deleted test file
paigen11 Mar 29, 2023
03bada3
Merge branch 'main' of github.com:blues/app-accelerators into main
paigen11 Mar 29, 2023
3498939
Merge branch 'main' into pn-update-notehub-js-auth-nf1
paigen11 Mar 29, 2023
883ebcf
refactor: dry up code into reusable functions
paigen11 Apr 3, 2023
7383646
remove console.logs
paigen11 Apr 4, 2023
3120757
Merge branch 'main' of github.com:blues/app-accelerators
paigen11 Apr 7, 2023
50d20c2
Merge branch 'main' into pn-update-notehub-js-auth-nf1
paigen11 Apr 7, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion 01-indoor-floor-level-tracker/web-app/.env.example
Expand Up @@ -25,7 +25,8 @@ NEXT_PUBLIC_BUILD_VERSION=''


# Backend
HUB_AUTH_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
HUB_CLIENT_ID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
HUB_CLIENT_SECRET=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
HUB_PROJECT_UID=app:00000000-0000-0000-0000-000000000000
HUB_FLEET_UID=fleet:00000000-0000-0000-0000-000000000000

Expand Down
10 changes: 7 additions & 3 deletions 01-indoor-floor-level-tracker/web-app/Config.ts
Expand Up @@ -8,11 +8,12 @@ const debugLog = console.log; // eslint-disable-line no-console
*/
const env = {
DEBUG_CONFIG: process.env.DEBUG_CONFIG,
HUB_AUTH_TOKEN: process.env.HUB_AUTH_TOKEN,
HUB_BASE_URL: process.env.HUB_BASE_URL,
HUB_GUI_URL: process.env.HUB_GUI_URL,
HUB_PROJECT_UID: process.env.HUB_PROJECT_UID,
HUB_FLEET_UID: process.env.HUB_FLEET_UID,
HUB_CLIENT_ID: process.env.HUB_CLIENT_ID,
HUB_CLIENT_SECRET: process.env.HUB_CLIENT_SECRET,
NEXT_PUBLIC_BUILD_VERSION: process.env.NEXT_PUBLIC_BUILD_VERSION,
NEXT_PUBLIC_COMPANY_NAME: process.env.NEXT_PUBLIC_COMPANY_NAME,
POSTGRES_USERNAME: process.env.POSTGRES_USERNAME,
Expand Down Expand Up @@ -63,8 +64,11 @@ const Config = {
get hubFleetUID() {
return requiredEnvVar("HUB_FLEET_UID");
},
get hubAuthToken() {
return requiredEnvVar("HUB_AUTH_TOKEN");
get hubClientId() {
return requiredEnvVar("HUB_CLIENT_ID");
},
get hubClientSecret() {
return requiredEnvVar("HUB_CLIENT_SECRET");
},
get hubBaseURL() {
return optionalEnvVar("HUB_BASE_URL", "https://api.notefile.net");
Expand Down
39 changes: 14 additions & 25 deletions 01-indoor-floor-level-tracker/web-app/README.md
@@ -1,6 +1,6 @@
# Indoor Floor-Level Tracker Web App

The Indoor Floor-Level Tracker’s web application allows you to view device data and
The Indoor Floor-Level Tracker’s web application allows you to view device data and
manage environment variables in a browser.

To get started, make sure you have a copy of this project’s repository locally,
Expand All @@ -12,10 +12,10 @@ IDE, and then complete the following steps.
- [Development](#development)

> **NOTE**: The Indoor Floor-Level Tracker’s web app uses the [Notehub API](https://dev.blues.io/guides-and-tutorials/using-the-notehub-api/)
to retrieve event data, which consumes [Notehub consumption credits](https://blues.io/pricing/).
You can change how frequently the app refreshes data from Notehub by altering
the `MS_REFETCH_INTERVAL` constant in the [`src/pages/index.tsx`](src/pages/index.tsx)
file.
> to retrieve event data, which consumes [Notehub consumption credits](https://blues.io/pricing/).
> You can change how frequently the app refreshes data from Notehub by altering
> the `MS_REFETCH_INTERVAL` constant in the [`src/pages/index.tsx`](src/pages/index.tsx)
> file.

## Dependencies

Expand All @@ -34,7 +34,7 @@ checking the following.

- **Windows**: Check for the docker (whale) icon in the system tray.
- **Linux/Mac**: Run the command `docker run hello-world` from your terminal. If everything
is working correctly you’ll see a confirmation message.
is working correctly you’ll see a confirmation message.

With Docker running, next open your web application in VS Code. Once you do, you will see
boxes that prompt you to install the extension **Remote - Containers**, and then to “Reopen
Expand Down Expand Up @@ -86,33 +86,22 @@ steps to do so.

1. Create a new `.env` file in the root folder of your project.
1. Copy the contents of your web app’s `.env.example` file, and paste
it in your new `.env` file.
it in your new `.env` file.
1. Change the required values in your `.env` to your own values using the steps
below.

### HUB_AUTH_TOKEN
### HUB_CLIENT_ID and HUB_CLIENT_SECRET

Blues reference web apps need access to your Notehub project in order to
access the Notehub API. An access token is used to authenticate the app.
access the Notehub API. A **client ID** and **client secret** unique to each Notehub user and Notehub project are used to generate an OAuth bearer token which is sent along with every request to the Notehub API.

To find retrieve an authentication token, execute the following command in your
terminal, replacing `YOUR_NOTEHUB_EMAIL` & `NOTEHUB_PASSWORD` with your own values.
To generate an authentication token, you'll need to supply a `client_id` and `client_secret` for the project in your `.env` file. You can follow these [instructions](https://dev.blues.io/reference/notehub-api/api-introduction/#authentication-with-oauth-bearer-tokens) to access a `client_id` and `client_secret` for your Notehub project.

```
curl -X POST -L 'https://api.notefile.net/auth/login' \
-d '{"username":"YOUR_NOTEHUB_EMAIL", "password": "NOTEHUB_PASSWORD"}'
```

When successful, you will see a response like

```
{"session_token":"BYj0bhMJwd3JucXE18f14Y3zMjQIoRfD"}
```

Copy the value after the colon to set the appropriate environment variable in `.env`, e.g.
Copy the values for each variable and set the appropriate environment variable in `.env`, e.g.

```
HUB_AUTH_TOKEN=BYj0bhMJwd3JucXE18f14Y3zMjQIoRfD
HUB_CLIENT_ID=3c0df26d-f9f4-4fd4-8a8d-847149a35790
HUB_CLIENT_SECRET=f7cfa681e4f471186fbd0bcf0abae08d4b5966d4afb1056f2355293df64d3aaa
```

### HUB_PROJECT_UID
Expand Down Expand Up @@ -163,4 +152,4 @@ a change, save the file, and notice how your browser automatically updates with
the change.

> **NOTE**: Changes to `.env` are **not** automatically reloaded, and require you
to stop the `yarn dev` with `ctrl+c` and to start `yarn dev` back up.
> to stop the `yarn dev` with `ctrl+c` and to start `yarn dev` back up.
3 changes: 2 additions & 1 deletion 01-indoor-floor-level-tracker/web-app/environment.d.ts
Expand Up @@ -2,7 +2,8 @@
declare global {
namespace NodeJS {
interface ProcessEnv {
HUB_AUTH_TOKEN: string;
HUB_CLIENT_ID: string;
HUB_CLIENT_SECRET: string;
HUB_BASE_URL: string;
HUB_GUI_URL: string;
HUB_PROJECT_UID: string;
Expand Down
5 changes: 3 additions & 2 deletions 01-indoor-floor-level-tracker/web-app/netlify.toml
Expand Up @@ -8,9 +8,10 @@
[template.environment]
NEXT_PUBLIC_COMPANY_NAME = "Company name for header"
HUB_PROJECT_UID = "Project UID of your Notehub project, e.g. app:123-456-789"
HUB_AUTH_TOKEN = "Notehub API authentication token"
HUB_CLIENT_ID = "Notehub API project ID"
HUB_CLIENT_SECRET = "Notehub API project secret"
HUB_BASE_URL = "(optional) Base URL representing the Notehub API"
HUB_FLEET_UID "Fleet UID for your Notehub project, e.g. fleet:987-654-321"
HUB_FLEET_UID = "Fleet UID for your Notehub project, e.g. fleet:987-654-321"
DEBUG_CONFIG = "(optional) Debug config? If not '', log environment and config on server"

[context.production]
Expand Down
3 changes: 2 additions & 1 deletion 01-indoor-floor-level-tracker/web-app/package.json
Expand Up @@ -16,10 +16,11 @@
},
"dependencies": {
"@ant-design/icons": "^4.7.0",
"@blues-inc/notehub-js": "^1.0.7",
"@blues-inc/notehub-js": "^1.0.11",
"@types/lodash": "^4.14.178",
"antd": "^4.19.1",
"axios": "^0.24.0",
"cookies-next": "^2.1.1",
"date-fns": "^2.27.0",
"http-status-codes": "^2.2.0",
"lodash": "^4.17.21",
Expand Down
Expand Up @@ -2,6 +2,7 @@ export const NextJsUrlManager = {
deviceNameUpdate: (deviceUID: string) => `/api/device/${deviceUID}/name`,
getDeviceTrackerData: () => `/api/device-trackers`,
setFleetTrackerConfig: () => `/api/fleet/tracker-config`,
handleAuthToken: () => `/api/auth-token`,
};

const DEFAULT = { NextJsUrlManager };
Expand Down
@@ -0,0 +1,9 @@
/* eslint-disable import/prefer-default-export */
import axios, { AxiosResponse } from "axios";
import { services } from "../services/ServiceLocatorClient";

export async function handleAuthToken() {
const endpoint = services().getUrlManager().handleAuthToken();
const response: AxiosResponse = await axios.get(endpoint);
return response.status === 200;
}
Expand Up @@ -2,12 +2,14 @@
import axios, { AxiosResponse } from "axios";
import { DeviceTracker } from "../services/AppModel";
import { services } from "../services/ServiceLocatorClient";
import { handleAuthToken } from "./authToken";

type GetDeviceTrackerResponse = {
deviceTrackers: DeviceTracker[];
};

export async function getDeviceTrackerData(): Promise<DeviceTracker[]> {
await handleAuthToken();
const endpoint = services().getUrlManager().getDeviceTrackerData();
const response: AxiosResponse = await axios.get<GetDeviceTrackerResponse>(
endpoint
Expand All @@ -16,6 +18,7 @@ export async function getDeviceTrackerData(): Promise<DeviceTracker[]> {
}

export async function changeDeviceName(deviceUID: string, name: string) {
await handleAuthToken();
const endpoint = services().getUrlManager().deviceNameUpdate(deviceUID);
const postBody = { name };
const response: AxiosResponse = await axios.post(endpoint, postBody);
Expand Down
@@ -1,13 +1,14 @@
/* eslint-disable import/prefer-default-export */
import axios from "axios";
import { services } from "../services/ServiceLocatorClient";
import { handleAuthToken } from "./authToken";

// generic function to pass all preformatted fleet env vars as objects for updates to Notehub
async function updateFleetEnvVar(fleetEnvVarToUpdate: object) {
await handleAuthToken();
const endpoint = services().getUrlManager().setFleetTrackerConfig();

const response = await axios.post<object>(endpoint, fleetEnvVarToUpdate);

return response.data;
}

Expand All @@ -28,7 +29,6 @@ export async function updateNoMovementThreshold(
};

const response = await updateFleetEnvVar(updateNoMovementThresholdObj);

return response;
}

Expand All @@ -38,6 +38,5 @@ export async function updateFloorHeightConfig(newFloorHeight: number) {
};

const response = await updateFleetEnvVar(updateFloorHeightConfigObj);

return response;
}
@@ -0,0 +1,29 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { getCookie, setCookie } from "cookies-next";
import { IncomingMessage, ServerResponse } from "http";
import { NextApiRequestCookies } from "next/dist/server/api-utils";
import { AuthToken } from "../services/AppModel";

export function fetchCookieAuthToken(
req: NextApiRequest | (IncomingMessage & { cookies: NextApiRequestCookies }),
res: NextApiResponse | ServerResponse
) {
return getCookie("authTokenObj", { req, res });
}

export function normalizeStringToAuthToken(cookieContents: string) {
const authTokenObject: AuthToken = JSON.parse(cookieContents);
return authTokenObject;
}

export function setCookieAuthToken(
authToken: AuthToken | string,
req: NextApiRequest | (IncomingMessage & { cookies: NextApiRequestCookies }),
res: NextApiResponse | ServerResponse
) {
let authStringObj = authToken;
if (typeof authToken === "object") {
authStringObj = JSON.stringify(authToken);
}
setCookie("authTokenObj", authStringObj, { req, res });
}
Expand Up @@ -2,4 +2,5 @@ export interface UrlManager {
getDeviceTrackerData(): string;
deviceNameUpdate(deviceUID: string): string;
setFleetTrackerConfig(): string;
handleAuthToken(): string;
}
8 changes: 3 additions & 5 deletions 01-indoor-floor-level-tracker/web-app/src/constants/http.ts
Expand Up @@ -10,10 +10,8 @@ const HTTP_STATUS = {
};

// HTTP headers
const HTTP_HEADER = {
CONTENT_TYPE: "Content-Type",
CONTENT_TYPE_JSON: "application/json",
SESSION_TOKEN: "X-SESSION-TOKEN",
const HTTP_AUTH = {
GRANT_TYPE: "client_credentials",
};

export { HTTP_STATUS, HTTP_HEADER };
export { HTTP_STATUS, HTTP_AUTH };
2 changes: 1 addition & 1 deletion 01-indoor-floor-level-tracker/web-app/src/constants/ui.ts
Expand Up @@ -9,7 +9,7 @@ const ERROR_MESSAGE = {
INTERNAL_ERROR:
"An internal error occurred. If this problem persists, contact <a href='https://discuss.blues.io' target='_blank' rel='noreferrer'>Blues Support</a>.",
UNAUTHORIZED:
"Authentication failed. Please ensure you have a valid HUB_AUTH_TOKEN environment variable.",
"Authentication failed. Please ensure you have valid HUB_CLIENT_ID and HUB_CLIENT_SECRET environment variables.",
DEVICE_NAME_CHANGE_FAILED:
"An error occurred changing your device’s name. If this problem persists, contact Blues support.",
UPDATE_FLEET_LIVE_STATUS_FAILED:
Expand Down
90 changes: 90 additions & 0 deletions 01-indoor-floor-level-tracker/web-app/src/pages/api/auth-token.ts
@@ -0,0 +1,90 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from "next";
import { ReasonPhrases, StatusCodes } from "http-status-codes";
import { ErrorWithCause } from "pony-cause";
import { CookieValueTypes } from "cookies-next";
import { serverLogError } from "./log";
import { services } from "../../services/ServiceLocatorServer";
import {
fetchCookieAuthToken,
setCookieAuthToken,
} from "../../authorization/cookieAuth";

function validateMethod(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== "GET") {
res.setHeader("Allow", ["GET"]);
res.status(StatusCodes.METHOD_NOT_ALLOWED);
res.json({ err: `Method ${req.method || "is undefined."} Not Allowed` });
return false;
}
return true;
}

export function doesAuthTokenExist(authToken: CookieValueTypes) {
if (authToken === undefined) {
return false;
}
return true;
}

export function isAuthTokenStillValid(authToken: CookieValueTypes) {
if (typeof authToken === "string") {
const appService = services().getAppService();
try {
const isAuthTokenValid = appService.checkAuthTokenValidity(authToken);
return isAuthTokenValid;
} catch (cause) {
throw new ErrorWithCause("Could not verify auth token validity ", {
cause,
});
}
}
return false;
}

async function performRequest() {
const appService = services().getAppService();

try {
return await appService.getAuthToken();
} catch (cause) {
throw new ErrorWithCause("Could not perform request", { cause });
}
}

export default async function authTokenHandler(
req: NextApiRequest,
res: NextApiResponse
) {
const authStringObj = fetchCookieAuthToken(req, res);

if (!validateMethod(req, res)) {
return;
}

// check if auth token exists, if it does, don't call the api
const authTokenExists = doesAuthTokenExist(authStringObj);
if (!authTokenExists) {
return;
}

// check if auth token is still valid, if it is, don't call the api
const authTokenStillValid = isAuthTokenStillValid(authStringObj);
if (!authTokenStillValid) {
return;
}

try {
const authToken = await performRequest();
setCookieAuthToken(authToken, req, res);
res.status(StatusCodes.OK).json({});
} catch (cause) {
res.status(StatusCodes.INTERNAL_SERVER_ERROR);
res.json({ err: ReasonPhrases.INTERNAL_SERVER_ERROR });
const e = new ErrorWithCause("Could not fetch auth token: ", {
cause,
});
serverLogError(e);
throw e;
}
}