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

feat: allow configurable tokens in nextjs app example #666

Merged
merged 8 commits into from
Jul 31, 2023
4 changes: 4 additions & 0 deletions examples/web/nextjs-chat/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ First, create a new file called `.env.local` that looks like
MOMENTO_AUTH_TOKEN=<Put your token here>
```

Second, go to the [config.ts file](./src/app/api/momento/token/config.ts) and configure the scope of permissions and the expiry duration for the tokens that the nextjs app will use to talk to the Momento service.

For example, you can restrict the permissions for these browser tokens so that they have read-only access or read-write access, and you can also restrict them to specific caches or topics.

Then, run the development server:

```bash
Expand Down
60 changes: 30 additions & 30 deletions examples/web/nextjs-chat/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions examples/web/nextjs-chat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"lint": "next lint"
},
"dependencies": {
"@gomomento/sdk": "^1.26.3",
"@gomomento/sdk-web": "^1.26.3",
"@gomomento/sdk": "^1.28.0",
"@gomomento/sdk-web": "^1.28.0",
"autoprefixer": "10.4.14",
"next": "13.4.8",
"react": "18.2.0",
Expand Down
52 changes: 52 additions & 0 deletions examples/web/nextjs-chat/src/app/api/momento/token/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {
AllDataReadWrite,
ExpiresIn,
TopicRole,
CacheRole,
TokenScope,
AllTopics,
AllCaches
} from "@gomomento/sdk";

/**
* First, set the scope of permissions for your tokens.
*
* AllDataReadWrite provides read and write permissions to all of your caches:
* export const tokenPermissions: TokenScope = AllDataReadWrite;
*
* You may also provide a bespoke list of permissions for each cache and topic that you have:
* export const tokenPermissions: TokenScope = {
* permissions: [
* {
* role: CacheRole.ReadWrite | CacheRole.ReadOnly,
* cache: AllCaches | "your-cache-name"
* },
* {
* role: TopicRole.PublishSubscribe | TopicRole.SubscribeOnly,
* cache: AllCaches | "your-cache-name",
* topic: AllTopics | "your-topic-name"
* }
* ]
* };
*
* More information here: https://docs.momentohq.com/develop/api-reference/auth-tokens#tokenscope-objects
*/
export const tokenPermissions: TokenScope = {
permissions: [
{
role: CacheRole.ReadWrite,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would actually be cool to be part of the api, maybe query params or something. The user could select a cache and a topic, and then we can make a request to get a scoped token for that specific cache and topic

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would that be similar to this?

Would there need to be a way to stack multiple permissions though?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I was thinking for the demo would be cool for it to just scope it down as low as possible. But we could also just accept a post body of all the permissions that the user wants the token to have

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the point of Anita's config file is to show app owners/maintainers how to restrict the scope (at app deploy time) so that the browser can't get tokens that have permissions to unexpected/unwanted caches. If we make it super dynamic then it's not really that different from just giving out an AllDataReadWrite token.

We can discuss this more for follow-up PRs, if there is something dynamic that is cool and worth demo'ing I'm open to talking about it! But for now this is more about showing how to make it secure.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the issue that I see here is that now the demo only works if a user has a cause default-cache already created

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I agree with that Matt, see my other comment and see if that addresses your concern

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tbh i wonder if we should just take the cache chooser out of the app. doesn't have to be in this PR but just thinking.

cache: "default-cache"
},
{
role: TopicRole.PublishSubscribe,
cache: "default-cache",
topic: AllTopics
}
]};

/**
* Second, set the TTL for your tokens in terms of seconds, minutes, hours,
* days, or using epoch format. You may also set tokens to never expire.
* More information here: https://docs.momentohq.com/develop/api-reference/auth-tokens#generateauthtoken-api
*/
export const tokenExpiresIn: ExpiresIn = ExpiresIn.minutes(5);
7 changes: 3 additions & 4 deletions examples/web/nextjs-chat/src/app/api/momento/token/route.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import {
AllDataReadWrite,
AuthClient,
CredentialProvider,
ExpiresIn,
GenerateAuthToken,
} from "@gomomento/sdk";
import { tokenPermissions, tokenExpiresIn } from "./config";

const authClient = new AuthClient({
credentialProvider: CredentialProvider.fromString({
Expand All @@ -15,8 +14,8 @@ const authClient = new AuthClient({
export const revalidate = 0;
export async function GET(_request: Request) {
const generateAuthTokenResponse = await authClient.generateAuthToken(
AllDataReadWrite,
ExpiresIn.minutes(5),
tokenPermissions,
tokenExpiresIn,
);

if (generateAuthTokenResponse instanceof GenerateAuthToken.Success) {
Expand Down