Skip to content

Commit

Permalink
feat - added google as auth provider
Browse files Browse the repository at this point in the history
  • Loading branch information
Akshara Hegde committed Nov 8, 2023
1 parent 06fb5f0 commit ccbafec
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 68 deletions.
10 changes: 10 additions & 0 deletions playground/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ const { loggedIn, session, clear } = useUserSession()
>
Login with Spotify
</UButton>
<UButton
v-if="!loggedIn || !session.user.google"
to="/auth/google"
icon="i-simple-icons-google"
external
color="gray"
size="xs"
>
Login with Google
</UButton>
<UButton
v-if="loggedIn"
color="gray"
Expand Down
9 changes: 5 additions & 4 deletions playground/auth.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
declare module '#auth-utils' {
interface UserSession {
user: {
spotify?: any
github?: any
}
loggedInAt: number
spotify?: any;
github?: any;
google?: any;
};
loggedInAt: number;
}
}
12 changes: 12 additions & 0 deletions playground/server/routes/auth/google.get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export default oauth.googleEventHandler({
async onSuccess(event, { user }) {
await setUserSession(event, {
user: {
google: user,
},
loggedInAt: Date.now()
})

return sendRedirect(event, '/')
}
})
5 changes: 5 additions & 0 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,10 @@ export default defineNuxtModule<ModuleOptions>({
clientId: '',
clientSecret: ''
})

runtimeConfig.oauth.google = defu(runtimeConfig.oauth.google, {
clientId: '',
clientSecret: ''
})
}
})
144 changes: 81 additions & 63 deletions src/runtime/server/lib/oauth/google.ts
Original file line number Diff line number Diff line change
@@ -1,122 +1,140 @@
import type { H3Event, H3Error } from 'h3'
import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from 'h3'
import { withQuery, parsePath } from 'ufo'
import { ofetch } from 'ofetch'
import { defu } from 'defu'
import { useRuntimeConfig } from '#imports'
import type { H3Event, H3Error } from "h3";
import {
eventHandler,
createError,
getQuery,
getRequestURL,
sendRedirect,
} from "h3";
import { withQuery, parsePath } from "ufo";
import { ofetch } from "ofetch";
import { defu } from "defu";
import { useRuntimeConfig } from "#imports";

export interface OAuthGoogleConfig {
/**
* Google OAuth Client ID
* @default process.env.NUXT_OAUTH_GOOGLE_CLIENT_ID
*/
clientId?: string
clientId?: string;

/**
/**
* Google OAuth Client Secret
* @default process.env.NUXT_OAUTH_GOOGLE_CLIENT_SECRET
*/
clientSecret?: string
clientSecret?: string;

/**
* Google OAuth Scope
* @default []
* @see https://developers.google.com/identity/protocols/oauth2/scopes#google-sign-in
* @example ['email', 'profile']
*/
scope?: string[]
scope?: string[];

/**
* Google OAuth Authorization URL
* @default 'https://accounts.google.com/o/oauth2/v2/auth'
*/
authorizationURL?: string
authorizationURL?: string;

/**
* Google OAuth Token URL
* @default 'https://oauth2.googleapis.com/token'
*/
tokenURL?: string
tokenURL?: string;

/**
* Redirect URL post authenticating via google
* @default '/auth/google'
*/
redirectUrl: "/auth/google";
}

interface OAuthConfig {
config?: OAuthGoogleConfig
onSuccess: (event: H3Event, result: { user: any, tokens: any }) => Promise<void> | void
onError?: (event: H3Event, error: H3Error) => Promise<void> | void
config?: OAuthGoogleConfig;
onSuccess: (
event: H3Event,
result: { user: any; tokens: any }
) => Promise<void> | void;
onError?: (event: H3Event, error: H3Error) => Promise<void> | void;
}

export function spotifyEventHandler({ config, onSuccess, onError }: OAuthConfig) {
export function googleEventHandler({
config,
onSuccess,
onError,
}: OAuthConfig) {
return eventHandler(async (event: H3Event) => {
// @ts-ignore
config = defu(config, useRuntimeConfig(event).oauth?.spotify, {
authorizationURL: 'https://accounts.google.com/o/oauth2/v2/auth',
tokenURL: 'https://oauth2.googleapis.com/token'
}) as OAuthGoogleConfig
const { code } = getQuery(event)
config = defu(config, useRuntimeConfig(event).oauth?.google, {
authorizationURL: "https://accounts.google.com/o/oauth2/v2/auth",
tokenURL: "https://oauth2.googleapis.com/token",
}) as OAuthGoogleConfig;
const { code } = getQuery(event);

if (!config.clientId) {
const error = createError({
statusCode: 500,
message: 'Missing NUXT_OAUTH_GOOGLE_CLIENT_ID env variables.'
})
if (!onError) throw error
return onError(event, error)
message: "Missing NUXT_OAUTH_GOOGLE_CLIENT_ID env variables.",
});
if (!onError) throw error;
return onError(event, error);
}

const redirectUrl = getRequestURL(event).href
const redirectUrl = getRequestURL(event).href;
if (!code) {
config.scope = config.scope || ['userinfo.email', 'userinfo.profile']
config.scope = config.scope || ["email", "profile"];
// Redirect to Google Oauth page
return sendRedirect(
event,
withQuery(config.authorizationURL as string, {
response_type: 'code',
response_type: "code",
client_id: config.clientId,
redirect_uri: redirectUrl,
scope: config.scope.join('%20')
scope: config.scope.join(" "),
})
)
);
}

const tokens: any = await ofetch(
config.tokenURL as string,
{
method: 'POST',
headers: {
Authorization: `Basic ${code}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
params: {
grant_type: 'authorization_code',
redirect_uri: parsePath(redirectUrl).pathname,
code
}
}
).catch(error => {
return { error }
})
const body: any = {
grant_type: "authorization_code",
redirect_uri: parsePath(redirectUrl).pathname,
client_id: config.clientId,
client_secret: config.clientSecret,
code,
};
const tokens: any = await ofetch(config.tokenURL as string, {
method: "POST",
body,
}).catch((error) => {
return { error };
});
if (tokens.error) {
const error = createError({
statusCode: 401,
message: `Google login failed: ${tokens.error?.data?.error_description || 'Unknown error'}`,
data: tokens
})
if (!onError) throw error
return onError(event, error)
message: `Google login failed: ${
tokens.error?.data?.error_description || "Unknown error"
}`,
data: tokens,
});
if (!onError) throw error;
return onError(event, error);
}

console.log('google tokens', tokens)
const accessToken = tokens.access_token
const user: any = {}
// await ofetch('https://api.spotify.com/v1/me', {
// headers: {
// Authorization: `Bearer ${accessToken}`
// }
// })
const accessToken = tokens.access_token;
const user: any = await ofetch(
"https://www.googleapis.com/oauth2/v3/userinfo",
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
}
);

return onSuccess(event, {
tokens,
user
})
})
user,
});
});
}
4 changes: 3 additions & 1 deletion src/runtime/server/utils/oauth.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { githubEventHandler } from '../lib/oauth/github'
import { googleEventHandler } from '../lib/oauth/google'
import { spotifyEventHandler } from '../lib/oauth/spotify'

export const oauth = {
githubEventHandler,
spotifyEventHandler
spotifyEventHandler,
googleEventHandler
}

0 comments on commit ccbafec

Please sign in to comment.