-
Notifications
You must be signed in to change notification settings - Fork 211
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add example of using
board.getIdToken
- Loading branch information
Showing
23 changed files
with
1,095 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
MIRO_CLIENT_ID="" | ||
MIRO_CLIENT_SECRET="" | ||
NEXT_PUBLIC_GIPHY_API_KEY="sXpGFDGZs0Dv1mmNFvYaGUvYwKX0PWIh" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
||
# dependencies | ||
/node_modules | ||
/.pnp | ||
.pnp.js | ||
.next | ||
|
||
# testing | ||
/coverage | ||
|
||
# misc | ||
.DS_Store | ||
*.pem | ||
.idea | ||
|
||
# debug | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
|
||
# local env files | ||
.env | ||
dist | ||
/store.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
# JWT Authentication with Miro | ||
|
||
This app shows how to add Miro user's signature to API requests using JWT token. | ||
Whenever a server endpoint needs to verify the user's identity, it can use the user ID JWT token provided by Miro. | ||
|
||
# 👨🏻💻 App Demo | ||
|
||
... | ||
|
||
# 📒 Table of Contents | ||
|
||
- [Included Features](#features) | ||
- [Tools and Technologies](#tools) | ||
- [Prerequisites](#prerequisites) | ||
- [Run the app locally](#run) | ||
- [Folder Structure](#folder) | ||
- [Contributing](#contributing) | ||
- [License](#license) | ||
|
||
# ⚙️ Included Features <a name="features"></a> | ||
|
||
- [Miro Web SDK](https://developers.miro.com/docs/web-sdk-reference) | ||
- [on(icon:click)](https://developers.miro.com/docs/ui_boardui#iconclick-event) | ||
- [openPanel(options)](https://developers.miro.com/docs/ui_boardui#openpanel) | ||
- [board.getIdToken()](https://developers.miro.com/docs/websdk-reference-board#getidtoken) | ||
- [board.createImage(...)](https://developers.miro.com/docs/websdk-reference-board#createimage) | ||
- [viewport.get()](https://developers.miro.com/docs/websdk-reference-viewport#get) | ||
|
||
# 🛠️ Tools and Technologies <a name="tools"></a> | ||
|
||
- [React](https://react.dev/) | ||
- [TypeScript](https://www.typescriptlang.org/) | ||
- [Next.js](https://nextjs.org/) | ||
- [Stripe CLI](https://stripe.com/docs/stripe-cli) | ||
- [Giphy SDK for Web](https://developers.giphy.com/docs/sdk/#web) | ||
|
||
# ✅ Prerequisites <a name="prerequisites"></a> | ||
|
||
- You have a [Miro account](https://miro.com/signup/). | ||
- You're [signed in to Miro](https://miro.com/login/). | ||
- Your Miro account has a [Developer team](https://developers.miro.com/docs/create-a-developer-team). | ||
- Your development environment includes [Node.js 18.17](https://nodejs.org/en/download) or a later version. | ||
- All examples use `npm` as a package manager and `npx` as a package runner. | ||
|
||
# 🏃🏽♂️ Run the app locally <a name="run"></a> | ||
|
||
1. Rename the ['.env.example' file](.env.example) to `.env`. You will need to create a Miro app and then add in the client ID and client secret. | ||
2. Run `npm install` to install dependencies. | ||
3. Run `npm start` to start developing. \ | ||
Your URL should be similar to this example: | ||
``` | ||
http://localhost:3000 | ||
``` | ||
4. Open the [app manifest editor](https://developers.miro.com/docs/manually-create-an-app#step-2-configure-your-app-in-miro) by clicking **Edit in Manifest**. \ | ||
In the app manifest editor, configure the app as follows: | ||
|
||
- [`appName`](): change it to `JWT demo` or whatever you want to name your app. | ||
- [`sdkUri`](https://developers.miro.com/docs/app-manifest#sdkuri): assign `http://localhost:3000` as a value for this property. | ||
- [`scopes`](https://developers.miro.com/docs/app-manifest#scopes): add the permission scopes that users need to grant the app when they install it. | ||
To enable the app to write to the board, add the following permissions: | ||
- `boards:read` | ||
|
||
5. Go back to your app home page, and under the `Permissions` section, you will see a blue button that says `Install app and get OAuth token`. Click that button. Then click on `Add` as shown in the video below. | ||
|
||
> ⚠️ We recommend to install your app on a [developer team](https://developers.miro.com/docs/create-a-developer-team) while you are developing or testing apps.⚠️ | ||
https://github.com/miroapp/app-examples/assets/10428517/1e6862de-8617-46ef-b265-97ff1cbfe8bf | ||
|
||
6. Go to your developer team, and open your boards. | ||
7. Click on the plus icon from the bottom section of your left sidebar. If you hover over it, it will say `More apps`. | ||
8. Search for your app `JWT demo` or whatever you chose to name it. Click on your app to use it, as shown in the video below. | ||
|
||
https://github.com/horeaporutiu/app-examples-template/assets/10428517/b23d9c4c-e785-43f9-a72e-fa5d82c7b019 | ||
|
||
# 🗂️ Folder structure <a name="folder"></a> | ||
|
||
``` | ||
./src | ||
│ └── components | ||
│ │── SdkInfo.tsx <-- React component to initialize the app in a Miro board. | ||
│ │── Grid.tsx <-- React component to render a grid with gifs. | ||
│ │── RecentGifs.tsx <-- React component to render recent gifs based on data from the app's server. | ||
│ └── Search.tsx <-- React component to render a gif search block. | ||
│ └── app | ||
│ │── layout.tsx <-- Root layout for the app. | ||
│ │── loading.tsx <-- Loading placeholder for the app. | ||
│ │── page.tsx <-- Index empty page to initialize the app. | ||
│ └── api | ||
│ └── recent | ||
│ └── route.ts <-- Route controller to get and update recent gifs for a user. | ||
│ └── panel | ||
│ └── page.ts <-- a page to render the app's panel. | ||
│ └── public | ||
│ └── favicon.ico <-- Icon for the web app. | ||
│ └── assets | ||
│ └── style.css <-- CSS styles for the app. | ||
│ └── hooks | ||
│ └── useRecent.ts <-- Custom hook to read and update recent gifs. | ||
│ └── utils | ||
│ │── api.ts <-- API utility to make requests to the app's server with Miro's user JWT header. | ||
│ │── miro.ts <-- Miro utility to interact with the Miro Web SDK. | ||
│ │── user.ts <-- User utility to get the user's ID from the JWT token. | ||
│ └── storage.ts <-- Implementation of storage logic. Will create a file `store.json` with userID and recent gifs. | ||
└── initMiro.ts <-- This is where the Node Client is initialized. | ||
``` | ||
|
||
# 🫱🏻🫲🏽 Contributing <a name="contributing"></a> | ||
|
||
If you want to contribute to this example, or any other Miro Open Source project, please review [Miro's contributing guide](https://github.com/miroapp/app-examples/blob/main/CONTRIBUTING.md). | ||
|
||
# 🪪 License <a name="license"></a> | ||
|
||
[MIT License](https://github.com/miroapp/app-examples/blob/main/LICENSE). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// https://vitejs.dev/guide/features.html#typescript-compiler-options | ||
/// <reference types="vite/client" /> | ||
/// <reference types="@mirohq/websdk-types" /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/// <reference types="next" /> | ||
/// <reference types="next/image-types/global" /> | ||
|
||
// NOTE: This file should not be edited | ||
// see https://nextjs.org/docs/basic-features/typescript for more information. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
{ | ||
"name": "jwt-auth", | ||
"version": "0.1.0", | ||
"license": "MIT", | ||
"scripts": { | ||
"build": "next build", | ||
"start": "next dev", | ||
"lint": "next lint" | ||
}, | ||
"dependencies": { | ||
"@giphy/react-components": "^9.3.0", | ||
"@mirohq/miro-api": "^2.0.0", | ||
"cookie": "^0.5.0", | ||
"dotenv": "^16.0.3", | ||
"jsonwebtoken": "9.0.2", | ||
"mirotone": "5", | ||
"next": "14.0.1", | ||
"react": "^18.2.0", | ||
"react-dom": "^18.2.0", | ||
"react-resize-observer": "1.1.1" | ||
}, | ||
"devDependencies": { | ||
"@mirohq/websdk-types": "latest", | ||
"@types/cookie": "^0.5.1", | ||
"@types/jsonwebtoken": "9.0.6", | ||
"@types/node": "^18.8.2", | ||
"@types/react": "^18.0.24", | ||
"typescript": "4.9.5" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { getUserIdFromRequest } from "../../../utils/user"; | ||
import { storage } from "../../../utils/storage"; | ||
import { NextResponse } from "next/server"; | ||
|
||
export async function GET() { | ||
const userId = getUserIdFromRequest(); | ||
const record = await storage.get(userId); | ||
|
||
return NextResponse.json({ recent: record?.recent ?? [] }); | ||
} | ||
|
||
export async function PUT(request: Request) { | ||
const userId = getUserIdFromRequest(); | ||
const gifId = await request.json(); | ||
|
||
const record = (await storage.get(userId)) ?? { recent: [] }; | ||
|
||
const recent = record.recent.filter((id) => id !== gifId); | ||
recent.unshift(gifId); | ||
|
||
record.recent = recent.slice(0, 4); | ||
|
||
await storage.set(userId, record); | ||
|
||
return NextResponse.json({ recent: record.recent }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import React, { PropsWithChildren } from "react"; | ||
import Script from "next/script"; | ||
import "../assets/style.css"; | ||
|
||
import { MiroSDKInit } from "../components/SDKInit"; | ||
|
||
export default function RootLayout({ children }: PropsWithChildren) { | ||
return ( | ||
<html> | ||
<body> | ||
<Script | ||
src="https://miro.com/app/static/sdk/v2/miro.js" | ||
strategy="beforeInteractive" | ||
/> | ||
<MiroSDKInit /> | ||
<div id="root">{children}</div> | ||
</body> | ||
</html> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
const Loading = () => { | ||
return <div>...Loading</div>; | ||
}; | ||
|
||
export default Loading; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default async function Page() { | ||
return null; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { RecentGifs } from "../../components/RecentGifs"; | ||
|
||
export default async function Page() { | ||
return <RecentGifs />; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
@import "mirotone/dist/styles.css"; | ||
|
||
body { | ||
display: flex; | ||
} | ||
|
||
#root { | ||
width: 100%; | ||
overflow: auto; | ||
margin: var(--space-medium); | ||
margin-top: 0; | ||
} | ||
|
||
.giphy-search-bar { | ||
margin-bottom: var(--space-xsmall); | ||
box-shadow: inset 0 0 0 1px var(--indigo400); | ||
padding-left: 1px; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { Grid as GiphyGrid } from "@giphy/react-components"; | ||
import ResizeObserver from "react-resize-observer"; | ||
import { ComponentProps, SyntheticEvent, useCallback, useState } from "react"; | ||
import { IGif } from "@giphy/js-types"; | ||
|
||
export type GridProps = { | ||
onSelect: (gif: IGif) => void; | ||
} & Pick<ComponentProps<typeof GiphyGrid>, "fetchGifs">; | ||
|
||
export function Grid({ fetchGifs, onSelect }: GridProps) { | ||
const [width, setWidth] = useState(320); | ||
|
||
const handleClick = useCallback( | ||
(gif: IGif, e: SyntheticEvent) => { | ||
e.preventDefault(); | ||
onSelect?.(gif); | ||
}, | ||
[onSelect], | ||
); | ||
|
||
return ( | ||
<> | ||
<GiphyGrid | ||
fetchGifs={fetchGifs} | ||
columns={2} | ||
width={width} | ||
onGifClick={handleClick} | ||
/> | ||
<ResizeObserver onResize={({ width }) => setWidth(width)} /> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
"use client"; | ||
|
||
import { IGif } from "@giphy/js-types"; | ||
import { GifsResult } from "@giphy/js-fetch-api"; | ||
import { useCallback } from "react"; | ||
import { createImage } from "../utils/miro"; | ||
import { useRecent } from "../hooks/useRecent"; | ||
import { Search } from "./Search"; | ||
import { Grid } from "./Grid"; | ||
|
||
function createGifsResult(recent: IGif[]): Promise<GifsResult> { | ||
return Promise.resolve({ | ||
data: recent, | ||
pagination: { | ||
total_count: recent.length, | ||
count: recent.length, | ||
offset: 0, | ||
}, | ||
meta: { | ||
status: 200, | ||
msg: "OK", | ||
response_id: "1", | ||
}, | ||
}); | ||
} | ||
|
||
export function RecentGifs() { | ||
const [recent, addRecent] = useRecent(); | ||
|
||
const handleSelectedGif = useCallback( | ||
async (gif: IGif) => { | ||
await createImage(gif); | ||
addRecent(gif); | ||
}, | ||
[addRecent], | ||
); | ||
|
||
if (!recent) return <p>Loading...</p>; | ||
|
||
const fetchGifs = () => createGifsResult(recent); | ||
const recentKey = recent.map((gif) => gif.id).join(","); | ||
|
||
return ( | ||
<div className="grid"> | ||
<div className="cs1 ce12"> | ||
<h4>Recent gifs:</h4> | ||
{recent.length === 0 ? ( | ||
<p>No recent GIFs. Try search below to add some!</p> | ||
) : ( | ||
<Grid | ||
key={recentKey} | ||
fetchGifs={fetchGifs} | ||
onSelect={handleSelectedGif} | ||
/> | ||
)} | ||
</div> | ||
|
||
<div className="cs1 ce12"> | ||
<h4>Search for more gifs:</h4> | ||
<Search onSelect={handleSelectedGif} /> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
"use client"; | ||
|
||
import { useEffect } from "react"; | ||
|
||
export const MiroSDKInit = () => { | ||
useEffect(() => { | ||
miro.board.ui.on("icon:click", async () => { | ||
await miro.board.ui.openPanel({ url: "/panel" }); | ||
}); | ||
}); | ||
|
||
return null; | ||
}; |
Oops, something went wrong.