Skip to content

Commit

Permalink
chore(nextjs-app): add thumbnail
Browse files Browse the repository at this point in the history
  • Loading branch information
emmanuelgautier committed Oct 10, 2024
1 parent 9d5f66d commit 30eafdf
Show file tree
Hide file tree
Showing 15 changed files with 278 additions and 58 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ jobs:
- workingDirectory: ./examples/react-spa
projectName: cerberauth-react-spa-oidc
directory: ./dist
- workingDirectory: ./examples/vue-spa
projectName: cerberauth-vue-spa-oidc
directory: ./dist
- workingDirectory: ./examples/angular-spa
projectName: cerberauth-angular-spa-oidc
directory: ./dist/angular-spa/browser
Expand Down
Binary file added examples/nextjs-app/thumbnail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions examples/vue-spa/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
VITE_OIDC_ISSUER="https://testid.cerberauth.com"
VITE_OIDC_CLIENT_ID=""
30 changes: 30 additions & 0 deletions examples/vue-spa/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
FROM node:lts as development

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .

ENV CI=true
ENV PORT=3000

CMD [ "npm", "start" ]

FROM development as builder

RUN npm run build

FROM nginx:alpine

# Copy config nginx
COPY --from=builder /app/.nginx/nginx.conf /etc/nginx/conf.d/default.conf

WORKDIR /usr/share/nginx/html

# Remove default nginx static assets
RUN rm -rf ./*

# Copy static assets from builder stage
COPY --from=builder /app/dist .
17 changes: 10 additions & 7 deletions examples/vue-spa/package-lock.json

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

3 changes: 2 additions & 1 deletion examples/vue-spa/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"pages:build": "vite build --mode production",
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --build --force",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"format": "prettier --write src/"
},
"dependencies": {
"oauth4webapi": "^3.0.0",
"vue": "^3.4.29",
"vue-auth3": "^1.0.10",
"vue-router": "^4.3.3"
},
"devDependencies": {
Expand Down
12 changes: 12 additions & 0 deletions examples/vue-spa/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import { RouterView } from 'vue-router'
import { useAuth } from '@/lib/auth'
const { handleLoginRedirect } = useAuth()
onMounted(async () => {
try {
await handleLoginRedirect()
} catch (error) {
console.error('OIDC callback error:', error)
}
})
</script>

<template>
Expand Down
Empty file.
6 changes: 0 additions & 6 deletions examples/vue-spa/src/components/Button.vue

This file was deleted.

13 changes: 0 additions & 13 deletions examples/vue-spa/src/drivers/testid.ts

This file was deleted.

49 changes: 49 additions & 0 deletions examples/vue-spa/src/lib/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { reactive, computed } from 'vue'
import {
login as oidcLogin,
handleLoginRedirect as oidcHandleLoginRedirect,
logout as oidcLogout,
} from './oidc'

interface AuthState {
isAuthenticated: boolean
user: null | { [key: string]: any }
}

const state = reactive<AuthState>({
isAuthenticated: false,
user: null,
})

export function useAuth() {
const login = () => {
oidcLogin()
}

const handleLoginRedirect = async () => {
const _user = await oidcHandleLoginRedirect()
if (!_user) {
return
}

state.isAuthenticated = true
state.user = _user
}

const logout = () => {
state.isAuthenticated = false
state.user = null
oidcLogout()
}

const isAuthenticated = computed(() => state.isAuthenticated)
const user = computed(() => state.user)

return {
login,
handleLoginRedirect,
logout,
isAuthenticated,
user,
}
}
156 changes: 156 additions & 0 deletions examples/vue-spa/src/lib/oidc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import * as oauth from 'oauth4webapi'

const webStorageKey = 'oidc:auth'
let accessToken: string | undefined
let idToken: string | undefined

if (!import.meta.env.VITE_OIDC_ISSUER) {
throw new Error('OIDC_ISSUER is not set')
}

if (!import.meta.env.VITE_OIDC_CLIENT_ID) {
throw new Error('OIDC_CLIENT_ID is not set')
}

const issuer = import.meta.env.VITE_OIDC_ISSUER
const clientId = import.meta.env.VITE_OIDC_CLIENT_ID

const issuerUrl = new URL(issuer)

const client: oauth.Client = {
client_id: clientId,
redirect_uris: [window.location.origin],
}
const clientAuth = oauth.None()

let _as: oauth.AuthorizationServer | undefined
const getAuthorizationServer = async (): Promise<oauth.AuthorizationServer> => {
if (_as) {
return _as
}

_as = await oauth.discoveryRequest(issuerUrl, { algorithm: 'oidc' })
.then((response) => oauth.processDiscoveryResponse(issuerUrl, response))
if (!_as) {
throw new Error('Invalid Authorization Server')
}

return _as
}

type LoginParams = {
scope?: string;
redirectUri?: string;
}

export const login = async (params?: LoginParams) => {
const as = await getAuthorizationServer()

const scope = params?.scope || 'openid email'
let redirectUri = params?.redirectUri
if (!redirectUri && Array.isArray(client.redirect_uris) && client.redirect_uris.length > 1) {
redirectUri = client.redirect_uris[0]?.toString()
}
redirectUri = redirectUri || window.location.origin

const code_challenge_method = 'S256'
/**
* The following MUST be generated for every redirect to the authorization_endpoint. You must store
* the code_verifier and nonce in the end-user session such that it can be recovered as the user
* gets redirected from the authorization server back to your application.
*/
const codeVerifier = oauth.generateRandomCodeVerifier()
const codeChallenge = await oauth.calculatePKCECodeChallenge(codeVerifier)
let nonce: string | undefined

const authorizationUrl = new URL(as.authorization_endpoint!)
authorizationUrl.searchParams.set('client_id', client.client_id)
authorizationUrl.searchParams.set('redirect_uri', redirectUri)
authorizationUrl.searchParams.set('response_type', 'code')
authorizationUrl.searchParams.set('scope', scope)
authorizationUrl.searchParams.set('code_challenge', codeChallenge)
authorizationUrl.searchParams.set('code_challenge_method', code_challenge_method)

let state: string | undefined
state = oauth.generateRandomState()
authorizationUrl.searchParams.set('state', state)

/**
* We cannot be sure the AS supports PKCE so we're going to use nonce too. Use of PKCE is
* backwards compatible even if the AS doesn't support it which is why we're using it regardless.
*/
if (as.code_challenge_methods_supported?.includes('S256') !== true) {
nonce = oauth.generateRandomNonce()
authorizationUrl.searchParams.set('nonce', nonce)
}

console.log('store code_verifier and nonce in the end-user session')
sessionStorage.setItem(webStorageKey, JSON.stringify({ codeVerifier, state, nonce, redirectUri }))

console.log('Redirecting to Authorization Server', authorizationUrl.toString())
window.location.assign(authorizationUrl.toString())
}

export const handleLoginRedirect = async (): Promise<oauth.UserInfoResponse | undefined> => {
// @ts-expect-error
const currentUrl: URL = new URL(window.location)
if (!currentUrl.searchParams.has('code')) {
return undefined
}

const as = await getAuthorizationServer()

const storage = sessionStorage.getItem(webStorageKey)
if (!storage) {
throw new Error('No stored codeVerifier and nonce found')
}
sessionStorage.removeItem(webStorageKey)
const { codeVerifier, state, nonce, redirectUri } = JSON.parse(storage)

const params = oauth.validateAuthResponse(as, client, currentUrl, state)

const authorizationResponse = await oauth.authorizationCodeGrantRequest(
as,
client,
clientAuth,
params,
redirectUri,
codeVerifier,
)

const authorizationCodeResult = await oauth.processAuthorizationCodeResponse(as, client, authorizationResponse, { expectedNonce: nonce })

console.log('Access Token Response', authorizationCodeResult)
accessToken = authorizationCodeResult.access_token
idToken = authorizationCodeResult.id_token
const claims = oauth.getValidatedIdTokenClaims(authorizationCodeResult)
console.log('ID Token Claims', claims)

const sub = claims?.sub
if (!sub) {
throw new Error('No sub claim in ID Token')
}

// UserInfo Request
const response = await oauth.userInfoRequest(as, client, accessToken)
const user = await oauth.processUserInfoResponse(as, client, sub, response)
console.log('UserInfo Response', user)

window.history.replaceState({}, document.title, redirectUri || window.location.origin)
return user
}

export const logout = async () => {
if (!idToken) {
throw new Error('No ID Token')
}

const as = await getAuthorizationServer()

const endSessionUrl = new URL(as.end_session_endpoint!)
endSessionUrl.searchParams.set('post_logout_redirect_uri', window.location.origin)
endSessionUrl.searchParams.set('id_token_hint', idToken)
console.log('Redirecting to End Session Endpoint', endSessionUrl.toString())

window.location.assign(endSessionUrl.toString())
}
17 changes: 1 addition & 16 deletions examples/vue-spa/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,10 @@
import './assets/main.css'

import { createApp } from 'vue'
import { createAuth } from 'vue-auth3'
import driverBearerToken from 'vue-auth3/drivers/auth/bearer-token'
import driverHttpFetch from 'vue-auth3/drivers/http/fetch'
import App from './App.vue'
import './drivers/testid'
import router from './router'

const auth = createAuth({
plugins: {
router,
},
drivers: {
auth: driverBearerToken,
http: driverHttpFetch,
},
})

const app = createApp(App)

app.use(router).use(auth)
// app.use(router)
app.use(router)
app.mount('#app')
4 changes: 2 additions & 2 deletions examples/vue-spa/src/router/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import HomeView from '@/views/HomeView.vue'

const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
Expand All @@ -8,7 +8,7 @@ const router = createRouter({
path: '/',
name: 'home',
component: HomeView
}
},
]
})

Expand Down
Loading

0 comments on commit 30eafdf

Please sign in to comment.