alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
99 lines (86 loc) • 2.63 kB
text/typescript
import { $context, AlephaError, z } from "alepha";
import type { IssuerPrimitive } from "alepha/security";
import type { OAuth2Profile } from "../providers/ServerAuthProvider.ts";
import {
$auth,
type LinkAccountFn,
type OidcOptions,
type WithLinkFn,
} from "./$auth.ts";
/**
* Already configured Facebook authentication primitive.
*
* Uses OAuth2 to authenticate users via their Facebook accounts.
* Upon successful authentication, it links the Facebook account to a user session.
*
* Environment Variables:
* - `FACEBOOK_CLIENT_ID`: The App ID obtained from the Meta Developer Console.
* - `FACEBOOK_CLIENT_SECRET`: The App Secret obtained from the Meta Developer Console.
*/
export const $authFacebook = (
realm: IssuerPrimitive & WithLinkFn,
options: Partial<OidcOptions> = {},
) => {
const { alepha } = $context();
const env = alepha.parseEnv(
z.object({
FACEBOOK_CLIENT_ID: z
.text({
description: "The App ID obtained from the Meta Developer Console.",
})
.optional(),
FACEBOOK_CLIENT_SECRET: z
.text({
description:
"The App Secret obtained from the Meta Developer Console.",
})
.optional(),
}),
);
const disabled = !env.FACEBOOK_CLIENT_ID || !env.FACEBOOK_CLIENT_SECRET;
const name = "facebook";
const account: LinkAccountFn | undefined =
options.account ?? (realm.link ? realm.link(name) : undefined);
if (!account) {
throw new AlephaError(
"Authentication requires a link function in the realm primitive.",
);
}
return $auth({
issuer: realm,
name,
oauth: {
clientId: env.FACEBOOK_CLIENT_ID!,
clientSecret: env.FACEBOOK_CLIENT_SECRET!,
authorization: "https://www.facebook.com/v25.0/dialog/oauth",
token: "https://graph.facebook.com/v25.0/oauth/access_token",
scope: "email",
userinfo: async (tokens) => {
const res = await fetch(
"https://graph.facebook.com/v25.0/me?fields=id,name,email,picture.width(200).height(200)",
{
headers: {
Authorization: `Bearer ${tokens.access_token}`,
},
},
).then((res) => res.json());
const user: OAuth2Profile = {
sub: res.id,
};
if (res.email) {
user.email = res.email;
}
if (res.name) {
user.name = res.name.trim();
}
if (res.picture?.data?.url) {
user.picture = res.picture.data.url;
}
return user;
},
...options,
account,
},
disabled,
});
};