W
Wasp-lang•2w ago
Sven

How to keep client credentials for a third party platform safe

The users of my SaaS are using a third party platform to sell goods, now this platform offers an API to which we as software can connect to using the clients credentials. It consists of a client-id and client-secret, which both can be found in the users account. Question: How do I keep these safe and also stored optimally, such that the Software can easily use the credentials per user and doesn't accidentally use the credentials of another user?
48 Replies
kapa.ai
kapa.ai•2w ago
Hi @Sven. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
MEE6
MEE6•2w ago
Wohooo @Sven, you just became a Waspeteer level 6!
kapa.ai
kapa.ai•2w ago
---------------------- To keep client credentials safe and optimally stored, you can consider using a managed client storage solution. Wasp is considering providing a helper for storing data locally on the clients, which abstracts away the storage implementation. This would wrap localStorage or sessionStorage and expose it to the user. Here's a proposed API for this feature:
import { storage } from '@wasp/storage';

// Sets 'value' at 'key'
storage.set('key', 'value')

// Gets value at 'key'
storage.get('key')

// Removes 'key'
storage.remove('key')

// Clears only things that were set by `storage.set`
storage.clear()
import { storage } from '@wasp/storage';

// Sets 'value' at 'key'
storage.set('key', 'value')

// Gets value at 'key'
storage.get('key')

// Removes 'key'
storage.remove('key')

// Clears only things that were set by `storage.set`
storage.clear()
For storing user sensitive data, Wasp could provide an API which could then automatically clear on log out. Something like:
storage.setPrivate('key', 'userSpecificData')
storage.setPrivate('key', 'userSpecificData')
This way, you can store the client-id and client-secret for each user in a secure and isolated manner, ensuring that the software doesn't accidentally use the credentials of another user. Please note that this feature is still under consideration and not yet implemented in Wasp. You can follow the progress on this Github issue (https://github.com/wasp-lang/wasp/issues/1082). In the meantime, you might want to consider using a similar approach with your own implementation or using a third-party library that provides similar functionality. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @Sven, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
Sven
Sven•2w ago
IS this a good option? I think it is not, as this would likely cause issues when deleting cache etc and having them put in the credentials again
miho
miho•2w ago
Kapa gave a bad answer IMHO. I guess the best way is to store the credentials in the database, but encrypt the values first.
Get values from the user -> encrypt them with a key that's defined in the env vars -> store the encrypted values in the DB
Get values from the user -> encrypt them with a key that's defined in the env vars -> store the encrypted values in the DB
miho
miho•2w ago
How'll implement the encryption part is up to you, but quick Google search gives me this: https://habtesoft.medium.com/encrypt-data-on-nodejs-e0a45c67c772
Medium
Encrypt data on Nodejs
Encrypting data is an essential security aspect, especially when working with sensitive data. Node.js provides a built-in crypto module…
Sven
Sven•2w ago
i have an env var with an encryption key, however i think i can't acces it on the webpage/frontend how would i acces it using process.env the same as how it is used here and encrypt the users filled in data.
const adminEmails = process.env.ADMIN_EMAILS?.split(',') || [];
const adminEmails = process.env.ADMIN_EMAILS?.split(',') || [];
@kapa.ai I have this piece of code in encryption.ts which encrypts my data. Both functions are imported into a .tsx file to make a certain string encrypted on a button click. Now it doesn't use the encryption key in my .env.server it just uses the 'your-default-key', how do i acces the encryption key securely?
import CryptoJS from 'crypto-js';

const secretKey = import.meta.env.REACT_APP_ENCRYPTION_KEY || 'your-default-key';
export const encrypt = (text: string): string => {
console.log(secretKey)
const ciphertext = CryptoJS.AES.encrypt(text, secretKey).toString();
return ciphertext;
};

export const decrypt = (ciphertext: string): string => {
const bytes = CryptoJS.AES.decrypt(ciphertext, secretKey);
const originalText = bytes.toString(CryptoJS.enc.Utf8);
return originalText;
};
import CryptoJS from 'crypto-js';

const secretKey = import.meta.env.REACT_APP_ENCRYPTION_KEY || 'your-default-key';
export const encrypt = (text: string): string => {
console.log(secretKey)
const ciphertext = CryptoJS.AES.encrypt(text, secretKey).toString();
return ciphertext;
};

export const decrypt = (ciphertext: string): string => {
const bytes = CryptoJS.AES.decrypt(ciphertext, secretKey);
const originalText = bytes.toString(CryptoJS.enc.Utf8);
return originalText;
};
kapa.ai
kapa.ai•2w ago
Let me check the knowledge base for you. One moment! :books: ---------------------- In Wasp, you can use environment variables to securely store and access sensitive data like encryption keys. However, you should not use import.meta.env to access environment variables in Wasp. Instead, you should use process.env. Here's how you can modify your code to use process.env:
import CryptoJS from 'crypto-js';

const secretKey = process.env.REACT_APP_ENCRYPTION_KEY || 'your-default-key';
export const encrypt = (text: string): string => {
console.log(secretKey)
const ciphertext = CryptoJS.AES.encrypt(text, secretKey).toString();
return ciphertext;
};

export const decrypt = (ciphertext: string): string => {
const bytes = CryptoJS.AES.decrypt(ciphertext, secretKey);
const originalText = bytes.toString(CryptoJS.enc.Utf8);
return originalText;
};
import CryptoJS from 'crypto-js';

const secretKey = process.env.REACT_APP_ENCRYPTION_KEY || 'your-default-key';
export const encrypt = (text: string): string => {
console.log(secretKey)
const ciphertext = CryptoJS.AES.encrypt(text, secretKey).toString();
return ciphertext;
};

export const decrypt = (ciphertext: string): string => {
const bytes = CryptoJS.AES.decrypt(ciphertext, secretKey);
const originalText = bytes.toString(CryptoJS.enc.Utf8);
return originalText;
};
You can then define your REACT_APP_ENCRYPTION_KEY in a .env file in your project root. This file should not be committed to your version control system. Instead, you should have a .env.example file that lists all the required environment variables without the actual values. Please note that environment variables that start with REACT_APP_ are injected into your application at build time. This means that anyone who can see your built application can see these values. Therefore, you should not use REACT_APP_ prefixed environment variables to store sensitive data like encryption keys. Instead, you should use environment variables that are only accessible on the server-side. In Wasp, there was an issue where the JWT secret was hardcoded in the back-end code. This was done for simplicity and to test how Wasp works, but it was recognized that this must be changed going forward. The JWT secret (and other secret values) should come from some config/env file in an encrypted way. The best way to implement this was yet to be determined at the time of the issue (https://github.com/wasp-lang/wasp/issues/129). Please ensure that you are following the best practices for managing sensitive data in your application. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @Sven, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
Sven
Sven•2w ago
@kapa.ai i get:
encryption.ts:3 Uncaught ReferenceError: process is not defined
at encryption.ts:3:19
encryption.ts:3 Uncaught ReferenceError: process is not defined
at encryption.ts:3:19
kapa.ai
kapa.ai•2w ago
Let me check the knowledge base for you. One moment! :books: ---------------------- The error Uncaught ReferenceError: process is not defined typically occurs when you're trying to access Node.js specific variables (like process.env) in the client-side code (React). In Wasp, environment variables are handled differently for the client and the server. For the client-side, environment variables are embedded into the client code during the build and shipping process, making them public and readable by anyone. Therefore, you should never store secrets in them (such as secret API keys). To enable Wasp to pick them up, client environment variables must be prefixed with REACT_APP_, for example: REACT_APP_SOME_VAR_NAME=.... You can read them from the client code like this:
console.log(import.meta.env.REACT_APP_SOME_VAR_NAME)
console.log(import.meta.env.REACT_APP_SOME_VAR_NAME)
On the other hand, server environment variables can store secret values (e.g. secret API keys) since they are not publicly readable. You can define them without any special prefix, such as SOME_VAR_NAME=.... You can read them in the server code like this:
console.log(process.env.SOME_VAR_NAME)
console.log(process.env.SOME_VAR_NAME)
In your case, if you're trying to access the encryption key in the client-side code, you should use import.meta.env.REACT_APP_ENCRYPTION_KEY. If you're accessing it in the server-side code, you should use process.env.REACT_APP_ENCRYPTION_KEY. Please refer to the Wasp documentation on environment variables (https://wasp-lang.dev/docs/project/env-vars) for more information. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @Sven, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
Sven
Sven•2w ago
@kapa.ai how would i do this then on the server side? could you provide me some code to set this up? I dont know in what file i would do this then...
kapa.ai
kapa.ai•2w ago
Let me check the knowledge base for you. One moment! :books: ---------------------- In Wasp, server-side code is typically written in server/ directory in your project root. You can create a new file in this directory, say encryption.js, and write your encryption and decryption functions there. Here's how you can do it:
// server/encryption.js
import CryptoJS from 'crypto-js';

const secretKey = process.env.ENCRYPTION_KEY || 'your-default-key';

export const encrypt = (text) => {
const ciphertext = CryptoJS.AES.encrypt(text, secretKey).toString();
return ciphertext;
};

export const decrypt = (ciphertext) => {
const bytes = CryptoJS.AES.decrypt(ciphertext, secretKey);
const originalText = bytes.toString(CryptoJS.enc.Utf8);
return originalText;
};
// server/encryption.js
import CryptoJS from 'crypto-js';

const secretKey = process.env.ENCRYPTION_KEY || 'your-default-key';

export const encrypt = (text) => {
const ciphertext = CryptoJS.AES.encrypt(text, secretKey).toString();
return ciphertext;
};

export const decrypt = (ciphertext) => {
const bytes = CryptoJS.AES.decrypt(ciphertext, secretKey);
const originalText = bytes.toString(CryptoJS.enc.Utf8);
return originalText;
};
Then, you can import these functions in your server-side code where you need to use them. Remember to add your ENCRYPTION_KEY to your .env.server file:
ENCRYPTION_KEY=your-encryption-key
ENCRYPTION_KEY=your-encryption-key
This file should be at the root of your project and should not be committed to your version control system. Instead, you should have a .env.server.example file that lists all the required environment variables without the actual values. Please note that this is a general guide and you might need to adjust the code according to your specific use case and the structure of your Wasp project. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @Sven, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
Sven
Sven•2w ago
But how would i use this to encrypt the strings in the input fields on my page @kapa.ai
kapa.ai
kapa.ai•2w ago
Let me check the knowledge base for you. One moment! :books: ---------------------- To encrypt the strings in the input fields on your page, you would need to make a request to the server-side function that performs the encryption. This is because the encryption key is stored on the server and should not be exposed to the client. Here's a general idea of how you can do it: 1. Create a Wasp action that calls the encryption function. You can define this in your main.wasp file:
// main.wasp
action Encrypt {
fn: import { encrypt } from "@server/encryption.js"
}
// main.wasp
action Encrypt {
fn: import { encrypt } from "@server/encryption.js"
}
2. In your React component, when the form is submitted, make a request to this action with the input string as a parameter. You can use the useMutation hook provided by Wasp to do this:
// YourComponent.tsx
import { useMutation } from '@wasp/queries'
import { Encrypt } from '@wasp/actions'

const YourComponent = () => {
const [encrypt] = useMutation(Encrypt)

const handleSubmit = async (event) => {
event.preventDefault()
const inputString = event.target.elements.inputString.value
const encryptedString = await encrypt({ inputString })
console.log(encryptedString)
}

return (
<form onSubmit={handleSubmit}>
<input name="inputString" type="text" />
<button type="submit">Encrypt</button>
</form>
)
}
// YourComponent.tsx
import { useMutation } from '@wasp/queries'
import { Encrypt } from '@wasp/actions'

const YourComponent = () => {
const [encrypt] = useMutation(Encrypt)

const handleSubmit = async (event) => {
event.preventDefault()
const inputString = event.target.elements.inputString.value
const encryptedString = await encrypt({ inputString })
console.log(encryptedString)
}

return (
<form onSubmit={handleSubmit}>
<input name="inputString" type="text" />
<button type="submit">Encrypt</button>
</form>
)
}
Please note that this is a general guide and you might need to adjust the code according to your specific use case and the structure of your Wasp project. Also, remember to handle errors and edge cases as needed. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @Sven, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
Sven
Sven•2w ago
Clearer as it can get, crazy thanks! you made me understand it really well! @kapa.ai Cannot find module '@wasp/actions' or its corresponding type declarations.ts(2307)
Want results from more Discord servers?
Add your server