8000 Provider revoke fixes #249 by Goncharo · Pull Request #37 · metriport/metriport · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Provider revoke fixes #249 #37

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

Merged
merged 1 commit into from
Jan 7, 2023
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
2 changes: 1 addition & 1 deletion api/app/src/default-error-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
defaultResponseBody({
status: status.INTERNAL_SERVER_ERROR,
title:
"Internal server error, please try again or reach out to help@metriport.ai",
"Internal server error, please try again or reach out to support@metriport.com",
})
);
};
72 changes: 47 additions & 25 deletions api/app/src/providers/withings.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import {
Activity,
Biometrics,
Sleep,
} from "@metriport/api";
import { Activity, Biometrics, Sleep } from "@metriport/api";
import { Axios } from "axios";
import dayjs from "dayjs";
import crypto from 'crypto';
import crypto from "crypto";

import { PROVIDER_WITHINGS } from "../shared/constants";
import { ConnectedUser } from "../models/connected-user";
Expand Down Expand Up @@ -69,7 +65,6 @@ export class Withings extends Provider implements OAuth2 {
return this.oauth.getAuthUri(state);
}


async getTokenFromAuthCode(code: string): Promise<string> {
// TODO: WILL UDPDATE IT JUST WASNT STRAIGHT FORWARD - NEEDED CLIENTID TO BE IN PARAMS WITH ACTION = REQUEST
const response = await axios.post(
Expand All @@ -90,43 +85,70 @@ export class Withings extends Provider implements OAuth2 {
}

async revokeProviderAccess(connectedUser: ConnectedUser) {
const providerData = getProviderDataFromConnectUserOrFail(connectedUser, PROVIDER_WITHINGS);
const providerData = getProviderDataFromConnectUserOrFail(
connectedUser,
PROVIDER_WITHINGS
);

const client_id = Config.getWithingsClientId();
const client_secret = Config.getWithingsClientSecret()
const client_secret = Config.getWithingsClientSecret();
const timestamp = dayjs().unix();

const nonce = await this.getNonce(client_id, client_secret, timestamp);
const status = await this.revokeToken(client_id, client_secret, timestamp, nonce, providerData.token)
const status = await this.revokeToken(
client_id,
client_secret,
timestamp,
nonce,
providerData.token
);

if (status === 0) {
await this.oauth.revokeLocal(connectedUser);
} else {
throw new Error("Withings Revoke failed");
}

throw new Error("Withings Revoke failed");
}

async getNonce(clientId: string, clientSecret: string, timestamp: number): Promise<string> {
const nonceAction = 'getnonce';
const nonceSignature = `${nonceAction},${clientId},${timestamp}`
const hashString = crypto.createHmac('sha256', clientSecret).update(nonceSignature).digest('hex')

const { data } = await axios.post(`${Withings.URL}/v2/signature?action=${nonceAction}&client_id=${clientId}&timestamp=${timestamp}&signature=${hashString}`);
async getNonce(
clientId: string,
clientSecret: string,
timestamp: number
): Promise<string> {
const nonceAction = "getnonce";
const nonceSignature = `${nonceAction},${clientId},${timestamp}`;
const hashString = crypto
.createHmac("sha256", clientSecret)
.update(nonceSignature)
.digest("hex");

const { data } = await axios.post(
`${Withings.URL}/v2/signature?action=${nonceAction}&client_id=${clientId}&timestamp=${timestamp}&signature=${hashString}`
);

return data.body.nonce
return data.body.nonce;
}

async revokeToken(clientId: string, clientSecret: string, timestamp: number, nonce: string, token: string): Promise<number> {
const revokeAction = 'revoke';
const revokeSignature = `${revokeAction},${clientId},${nonce}`
const revokeHashString = crypto.createHmac('sha256', clientSecret).update(revokeSignature).digest('hex')
async revokeToken(
clientId: string,
clientSecret: string,
timestamp: number,
nonce: string,
token: string
): Promise<number> {
const revokeAction = "revoke";
const revokeSignature = `${revokeAction},${clientId},${nonce}`;
const revokeHashString = crypto
.createHmac("sha256", clientSecret)
.update(revokeSignature)
.digest("hex");
const parsedToken = JSON.parse(token);

const { data } = await axios.post(`
${Withings.URL}/${Withings.TOKEN_PATH}?action=revoke&client_id=${clientId}&timestamp=${timestamp}&signature=${revokeHashString}&userid=${parsedToken.userid}&nonce=${nonce}
`)
`);

return data.status
return data.status;
}

async fetchActivityData(
Expand Down
24 changes: 18 additions & 6 deletions api/app/src/routes/user.ts
8000
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import { ConsumerHealthDataType } from "../providers/provider";
import { Config } from "../shared/config";
import { getProviderDataForType } from "./helpers/provider-route-helper";
import { asyncHandler, getCxIdOrFail, getUserIdFromQueryOrFail } from "./util";
import { Constants, providerOAuth2OptionsSchema } from "../shared/constants";
import {
Constants,
providerOAuth1OptionsSchema,
providerOAuth2OptionsSchema,
} from "../shared/constants";
import BadRequestError from "../errors/bad-request";

const router = Router();
Expand Down Expand Up @@ -131,29 +135,37 @@ router.get(
router.delete(
"/revoke",
asyncHandler(async (req: Request, res: Response) => {

const userId = getUserIdFromQueryOrFail(req);
const cxId = getCxIdOrFail(req);
const connectedUser = await getConnectedUserOrFail({ id: userId, cxId })
const connectedUser = await getConnectedUserOrFail({ id: userId, cxId });

const providerOAuth2 = providerOAuth2OptionsSchema.safeParse(
req.query.provider
);

if (providerOAuth2.success) {
// TODO #249: implement garmin revoke support
// const providerOAuth1 = providerOAuth1OptionsSchema.safeParse(
// req.query.provider
// );

if (providerOAuth2.success) {
await Constants.PROVIDER_OAUTH2_MAP[
providerOAuth2.data
].revokeProviderAccess(connectedUser);

return res.sendStatus(200);
// } else if (providerOAuth1.success) {
// // await Constants.PROVIDER_OAUTH1_MAP[
// // providerOAuth1.data
// // ].deregister(connectedUser);
} else {
throw new BadRequestError(`Provider not supported: ${req.query.provider}`);
throw new BadRequestError(
`Provider not supported: ${req.query.provider}`
);
}
})
);


/* /user/connect */
router.get("/connect", async (req: Request, res: Response) => {
// TODO: get all users currently connected to the API to display on dev dash
Expand Down
16 changes: 8 additions & 8 deletions docs/more-info/our-integrations.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ description:

## Supported Providers

| Providers | Status | Activity | Biometrics | Body | Nutrition | Sleep | User |
| ---------- | ------ | -------- | ---------- | ---- | --------- | ----- | ---- |
| Cronometer | Active | No | No | No | Yes | No | No |
| Fitbit | Active | Yes | Yes | Yes | Yes | Yes | Yes |
| Garmin | Active | Yes | Yes | Yes | No | Yes | No |
| Oura | Active | Yes | Yes | Yes | No | Yes | Yes |
| Whoop | Active | Yes | Yes | Yes | No | Yes | Yes |
| Withings | Active | Yes | Yes | No | Yes | Yes | No |
| Providers | Status | Activity | Biometrics | Body | Nutrition | Sleep | User | Webhooks |
| ---------- | ------ | -------- | ---------- | ---- | --------- | ----- | ---- | -------- |
| Cronometer | Active | No | No | No | Yes | No | No | No |
| Fitbit | Active | Yes | Yes | Yes | Yes | Yes | Yes | No |
| Garmin | Active | Yes | Yes | Yes | No | Yes | No | Yes |
| Oura | Active | Yes | Yes | Yes | No | Yes | Yes | No |
| Whoop | Active | Yes | Yes | Yes | No | Yes | Yes | No |
| Withings | Active | Yes | Yes | No | Yes | Yes | No | No |

## Coming Soon

Expand Down
2 changes: 2 additions & 0 deletions docs/user/revoke-provider-access.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ api: "DELETE /user/revoke"

<ParamField query="provider" type="string" required>
The provider you would like to revoke access to.

Can be one of: `cronometer`, `fitbit`, `oura`, `whoop`, or `withings`.
</ParamField>
0