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

Add app login page #21

Draft
wants to merge 14 commits into
base: dev
Choose a base branch
from
5 changes: 4 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ RUN mkdir -p /app

WORKDIR /app

ENV NPM_CONFIG_CACHE=/home/node/.npm

COPY package*.json ./

RUN mkdir -p $NPM_CONFIG_CACHE && chown -R node:node $NPM_CONFIG_CACHE

RUN npm install

COPY . .
Expand All @@ -15,5 +19,4 @@ RUN npm run build

EXPOSE 3000

# Start the app on port 4455 as recommended by ory
CMD ["npm", "start", "--", "-p", "3000"]
56 changes: 42 additions & 14 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"next": "12.1.6",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-qr-code": "^2.0.15",
"react-toastify": "^8.0.3",
"styled-components": "^5.3.1",
"typescript": "^4.4.2"
Expand Down
2 changes: 1 addition & 1 deletion pages/api/consent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
const acceptResponse = await hydra.acceptOAuth2ConsentRequest({
consentChallenge,
acceptOAuth2ConsentRequest: {
grant_scope: grantScope,
grant_scope: session.access_token.scope,
grant_access_token_audience: body.requested_access_token_audience,
session,
remember: Boolean(remember),
Expand Down
116 changes: 116 additions & 0 deletions pages/apps.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { SettingsFlow } from "@ory/client"
import type { NextPage } from "next"
import Head from "next/head"
import Link from "next/link"
import { useRouter } from "next/router"
import { ReactNode, useEffect, useState } from "react"
import QRCode from "react-qr-code"

import { ActionCard, CenterLink, Methods, CardTitle } from "../pkg"
import ory from "../pkg/sdk"

interface Props {
flow?: SettingsFlow
only?: Methods
}

function AppLoginCard({ children }: Props & { children: ReactNode }) {
return (
<ActionCard wide className="cardMargin">
{children}
</ActionCard>
)
}

const Apps: NextPage = () => {
const router = useRouter()
const DefaultHydraUrl =
process.env.HYDRA_PUBLIC_URL || "http://localhost:4444"
const { flow: flowId, return_to: returnTo } = router.query
const [traits, setTraits] = useState<any>()
const [projects, setProjects] = useState<any>([])

const handleNavigation = () => {
router.replace("/fitbit")
}

useEffect(() => {
// If the router is not ready yet, or we already have a flow, do nothing.
if (!router.isReady) {
return
}

// Otherwise we initialize it
ory.toSession().then(({ data }) => {
const traits = data?.identity?.traits
setTraits(traits)
setProjects(traits.projects)
})
}, [flowId, router, router.isReady, returnTo])

return (
<>
<Head>
<title>App Login</title>
<meta name="description" content="NextJS + React + Vercel + Ory" />
</Head>
<AppLoginCard>
<CardTitle>App Login</CardTitle>
<QrForm
projects={projects}
baseUrl={DefaultHydraUrl}
navigate={handleNavigation}
/>
</AppLoginCard>
<ActionCard wide>
<Link href="/" passHref>
<CenterLink>Go back</CenterLink>
</Link>
</ActionCard>
</>
)
}

interface QrFormProps {
projects: any[]
baseUrl: string
navigate: any
}

const QrForm: React.FC<QrFormProps> = ({ projects, baseUrl, navigate }) => {
if (projects) {
return (
<div className="center">
{projects.map((project) => (
<div key={project.id} className="project-form">
<h3>{project.name}</h3>
<label className="inputLabel">Active App</label>
<p>Scan the QR code below with your app.</p>
<QRCode value={baseUrl + "?projectId=" + project.id} size={140} />
<br />
<br />
<button className="col-xs-4">Login with Active App</button>
<br />
<br />
<br />
<div>
<label className="inputLabel">Connect Your Fitbit</label>
<p>Click the button below to redirect to Fitbit.</p>
<button className="col-xs-4" onClick={navigate}>
Login with Fitbit
</button>
</div>
</div>
))}
</div>
)
} else {
return (
<div className="center">
<label className="inputLabel">No projects.</label>
</div>
)
}
}

export default Apps
7 changes: 5 additions & 2 deletions pages/consent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ const Consent = () => {
const [consent, setConsent] = useState<any>(null)
const [identity, setIdentity] = useState<any>(null)
const [csrfToken, setCsrfToken] = useState<string>("")
const [isLoading, setIsLoading] = useState<boolean>(false)

useEffect(() => {
const { consent_challenge } = router.query

const fetchSessionAndConsent = async () => {
setIsLoading(true)
try {
const sessionResponse = await ory.toSession()
const sessionData = sessionResponse.data
Expand Down Expand Up @@ -56,12 +58,13 @@ const Consent = () => {
if (skipData.error) {
throw new Error(skipData.error)
}

router.push(skipData.redirect_to)
return
}
} catch (error) {
console.error("Error fetching session or consent:", error)
}
setIsLoading(false)
}

if (router.query.consent_challenge) {
Expand Down Expand Up @@ -113,7 +116,7 @@ const Consent = () => {
}
}

if (!consent) {
if (!consent || isLoading) {
return <div>Loading...</div>
}

Expand Down
Loading
Loading