@setzkasten-cms/auth 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +37 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +155 -0
- package/package.json +23 -0
- package/src/github-oauth.ts +122 -0
- package/src/google-oauth.ts +104 -0
- package/src/index.ts +15 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Setzkasten Community License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lilapixel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to use,
|
|
7
|
+
copy, modify, merge, publish, and distribute the Software, subject to the
|
|
8
|
+
following conditions:
|
|
9
|
+
|
|
10
|
+
1. The above copyright notice and this permission notice shall be included in
|
|
11
|
+
all copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
2. The Software may not be used for commercial purposes without a separate
|
|
14
|
+
commercial license from the copyright holder. "Commercial purposes" means
|
|
15
|
+
any use of the Software that is primarily intended for or directed toward
|
|
16
|
+
commercial advantage or monetary compensation. This includes, but is not
|
|
17
|
+
limited to:
|
|
18
|
+
- Using the Software to manage content for a commercial website or product
|
|
19
|
+
- Offering the Software as part of a paid service
|
|
20
|
+
- Using the Software within a for-profit organization
|
|
21
|
+
|
|
22
|
+
3. Non-commercial use is permitted without restriction. This includes:
|
|
23
|
+
- Personal projects
|
|
24
|
+
- Open source projects
|
|
25
|
+
- Educational and academic use
|
|
26
|
+
- Non-profit organizations
|
|
27
|
+
|
|
28
|
+
4. A commercial license ("Enterprise License") may be obtained by contacting
|
|
29
|
+
Lilapixel at hello@lilapixel.de.
|
|
30
|
+
|
|
31
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
32
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
33
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
34
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
35
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
36
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
37
|
+
SOFTWARE.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { AuthProvider } from '@setzkasten-cms/core';
|
|
2
|
+
export { AuthProvider, AuthSession, AuthUser } from '@setzkasten-cms/core';
|
|
3
|
+
|
|
4
|
+
interface GitHubOAuthConfig {
|
|
5
|
+
clientId: string;
|
|
6
|
+
clientSecret: string;
|
|
7
|
+
redirectUri: string;
|
|
8
|
+
allowedEmails?: readonly string[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* GitHub OAuth adapter using Arctic.
|
|
12
|
+
* Handles the OAuth flow and session management server-side.
|
|
13
|
+
*/
|
|
14
|
+
declare function createGitHubAuth(config: GitHubOAuthConfig): AuthProvider;
|
|
15
|
+
|
|
16
|
+
interface GoogleOAuthConfig {
|
|
17
|
+
clientId: string;
|
|
18
|
+
clientSecret: string;
|
|
19
|
+
redirectUri: string;
|
|
20
|
+
allowedEmails?: readonly string[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Google OAuth adapter using Arctic.
|
|
24
|
+
*/
|
|
25
|
+
declare function createGoogleAuth(config: GoogleOAuthConfig): AuthProvider;
|
|
26
|
+
|
|
27
|
+
export { type GitHubOAuthConfig, type GoogleOAuthConfig, createGitHubAuth, createGoogleAuth };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// src/github-oauth.ts
|
|
2
|
+
import { GitHub } from "arctic";
|
|
3
|
+
import { ok, err, authError } from "@setzkasten-cms/core";
|
|
4
|
+
function createGitHubAuth(config) {
|
|
5
|
+
const github = new GitHub(config.clientId, config.clientSecret, config.redirectUri);
|
|
6
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
7
|
+
return {
|
|
8
|
+
async getSession() {
|
|
9
|
+
for (const session of sessions.values()) {
|
|
10
|
+
if (session.expiresAt > Date.now()) {
|
|
11
|
+
return ok(session);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return ok(null);
|
|
15
|
+
},
|
|
16
|
+
getLoginUrl() {
|
|
17
|
+
const state = generateState();
|
|
18
|
+
const scopes = ["user:email"];
|
|
19
|
+
const url = github.createAuthorizationURL(state, scopes);
|
|
20
|
+
return url.toString();
|
|
21
|
+
},
|
|
22
|
+
async handleCallback(code) {
|
|
23
|
+
try {
|
|
24
|
+
const tokens = await github.validateAuthorizationCode(code);
|
|
25
|
+
const accessToken = tokens.accessToken();
|
|
26
|
+
const userResponse = await fetch("https://api.github.com/user", {
|
|
27
|
+
headers: { Authorization: `Bearer ${accessToken}` }
|
|
28
|
+
});
|
|
29
|
+
if (!userResponse.ok) {
|
|
30
|
+
return err(authError("Failed to fetch GitHub user info"));
|
|
31
|
+
}
|
|
32
|
+
const userData = await userResponse.json();
|
|
33
|
+
let email = userData.email;
|
|
34
|
+
if (!email) {
|
|
35
|
+
const emailResponse = await fetch("https://api.github.com/user/emails", {
|
|
36
|
+
headers: { Authorization: `Bearer ${accessToken}` }
|
|
37
|
+
});
|
|
38
|
+
if (emailResponse.ok) {
|
|
39
|
+
const emails = await emailResponse.json();
|
|
40
|
+
const primary = emails.find((e) => e.primary && e.verified);
|
|
41
|
+
email = primary?.email ?? emails[0]?.email ?? null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (!email) {
|
|
45
|
+
return err(authError("No email found on GitHub account"));
|
|
46
|
+
}
|
|
47
|
+
if (config.allowedEmails && !config.allowedEmails.includes(email)) {
|
|
48
|
+
return err(authError(`Email ${email} is not allowed`));
|
|
49
|
+
}
|
|
50
|
+
const user = {
|
|
51
|
+
id: String(userData.id),
|
|
52
|
+
email,
|
|
53
|
+
name: userData.name ?? void 0,
|
|
54
|
+
avatarUrl: userData.avatar_url,
|
|
55
|
+
provider: "github"
|
|
56
|
+
};
|
|
57
|
+
const session = {
|
|
58
|
+
user,
|
|
59
|
+
expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1e3
|
|
60
|
+
// 7 days
|
|
61
|
+
};
|
|
62
|
+
sessions.set(user.id, session);
|
|
63
|
+
return ok(session);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
return err(
|
|
66
|
+
authError(
|
|
67
|
+
`GitHub OAuth failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
68
|
+
)
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
async logout() {
|
|
73
|
+
sessions.clear();
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function generateState() {
|
|
78
|
+
const array = new Uint8Array(32);
|
|
79
|
+
crypto.getRandomValues(array);
|
|
80
|
+
return Array.from(array, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/google-oauth.ts
|
|
84
|
+
import { Google } from "arctic";
|
|
85
|
+
import { ok as ok2, err as err2, authError as authError2 } from "@setzkasten-cms/core";
|
|
86
|
+
function createGoogleAuth(config) {
|
|
87
|
+
const google = new Google(config.clientId, config.clientSecret, config.redirectUri);
|
|
88
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
89
|
+
return {
|
|
90
|
+
async getSession() {
|
|
91
|
+
for (const session of sessions.values()) {
|
|
92
|
+
if (session.expiresAt > Date.now()) {
|
|
93
|
+
return ok2(session);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return ok2(null);
|
|
97
|
+
},
|
|
98
|
+
getLoginUrl() {
|
|
99
|
+
const state = generateState2();
|
|
100
|
+
const codeVerifier = generateState2();
|
|
101
|
+
const scopes = ["openid", "email", "profile"];
|
|
102
|
+
const url = google.createAuthorizationURL(state, codeVerifier, scopes);
|
|
103
|
+
return url.toString();
|
|
104
|
+
},
|
|
105
|
+
async handleCallback(code) {
|
|
106
|
+
try {
|
|
107
|
+
const codeVerifier = "";
|
|
108
|
+
const tokens = await google.validateAuthorizationCode(code, codeVerifier);
|
|
109
|
+
const accessToken = tokens.accessToken();
|
|
110
|
+
const userResponse = await fetch(
|
|
111
|
+
"https://www.googleapis.com/oauth2/v2/userinfo",
|
|
112
|
+
{ headers: { Authorization: `Bearer ${accessToken}` } }
|
|
113
|
+
);
|
|
114
|
+
if (!userResponse.ok) {
|
|
115
|
+
return err2(authError2("Failed to fetch Google user info"));
|
|
116
|
+
}
|
|
117
|
+
const userData = await userResponse.json();
|
|
118
|
+
if (config.allowedEmails && !config.allowedEmails.includes(userData.email)) {
|
|
119
|
+
return err2(authError2(`Email ${userData.email} is not allowed`));
|
|
120
|
+
}
|
|
121
|
+
const user = {
|
|
122
|
+
id: userData.id,
|
|
123
|
+
email: userData.email,
|
|
124
|
+
name: userData.name,
|
|
125
|
+
avatarUrl: userData.picture,
|
|
126
|
+
provider: "google"
|
|
127
|
+
};
|
|
128
|
+
const session = {
|
|
129
|
+
user,
|
|
130
|
+
expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1e3
|
|
131
|
+
};
|
|
132
|
+
sessions.set(user.id, session);
|
|
133
|
+
return ok2(session);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
return err2(
|
|
136
|
+
authError2(
|
|
137
|
+
`Google OAuth failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
138
|
+
)
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
async logout() {
|
|
143
|
+
sessions.clear();
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function generateState2() {
|
|
148
|
+
const array = new Uint8Array(32);
|
|
149
|
+
crypto.getRandomValues(array);
|
|
150
|
+
return Array.from(array, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
151
|
+
}
|
|
152
|
+
export {
|
|
153
|
+
createGitHubAuth,
|
|
154
|
+
createGoogleAuth
|
|
155
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@setzkasten-cms/auth",
|
|
3
|
+
"version": "0.4.2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"import": "./src/index.ts",
|
|
8
|
+
"types": "./src/index.ts"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"src"
|
|
14
|
+
],
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"arctic": "^3.5.0",
|
|
17
|
+
"@setzkasten-cms/core": "0.4.2"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"typecheck": "tsc --noEmit"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { GitHub } from 'arctic'
|
|
2
|
+
import type { AuthProvider, AuthUser, AuthSession } from '@setzkasten-cms/core'
|
|
3
|
+
import { ok, err, authError } from '@setzkasten-cms/core'
|
|
4
|
+
|
|
5
|
+
export interface GitHubOAuthConfig {
|
|
6
|
+
clientId: string
|
|
7
|
+
clientSecret: string
|
|
8
|
+
redirectUri: string
|
|
9
|
+
allowedEmails?: readonly string[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* GitHub OAuth adapter using Arctic.
|
|
14
|
+
* Handles the OAuth flow and session management server-side.
|
|
15
|
+
*/
|
|
16
|
+
export function createGitHubAuth(config: GitHubOAuthConfig): AuthProvider {
|
|
17
|
+
const github = new GitHub(config.clientId, config.clientSecret, config.redirectUri)
|
|
18
|
+
|
|
19
|
+
// In-memory session store (for server-side use in Astro API routes)
|
|
20
|
+
const sessions = new Map<string, AuthSession>()
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
async getSession() {
|
|
24
|
+
// In production, read session from cookie/token
|
|
25
|
+
// Here we return the first active session for simplicity
|
|
26
|
+
for (const session of sessions.values()) {
|
|
27
|
+
if (session.expiresAt > Date.now()) {
|
|
28
|
+
return ok(session)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return ok(null)
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
getLoginUrl() {
|
|
35
|
+
const state = generateState()
|
|
36
|
+
const scopes = ['user:email']
|
|
37
|
+
const url = github.createAuthorizationURL(state, scopes)
|
|
38
|
+
return url.toString()
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
async handleCallback(code: string) {
|
|
42
|
+
try {
|
|
43
|
+
const tokens = await github.validateAuthorizationCode(code)
|
|
44
|
+
const accessToken = tokens.accessToken()
|
|
45
|
+
|
|
46
|
+
// Fetch user info from GitHub API
|
|
47
|
+
const userResponse = await fetch('https://api.github.com/user', {
|
|
48
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
if (!userResponse.ok) {
|
|
52
|
+
return err(authError('Failed to fetch GitHub user info'))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const userData = (await userResponse.json()) as {
|
|
56
|
+
id: number
|
|
57
|
+
email: string | null
|
|
58
|
+
name: string | null
|
|
59
|
+
avatar_url: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Fetch primary email if not public
|
|
63
|
+
let email = userData.email
|
|
64
|
+
if (!email) {
|
|
65
|
+
const emailResponse = await fetch('https://api.github.com/user/emails', {
|
|
66
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
67
|
+
})
|
|
68
|
+
if (emailResponse.ok) {
|
|
69
|
+
const emails = (await emailResponse.json()) as Array<{
|
|
70
|
+
email: string
|
|
71
|
+
primary: boolean
|
|
72
|
+
verified: boolean
|
|
73
|
+
}>
|
|
74
|
+
const primary = emails.find((e) => e.primary && e.verified)
|
|
75
|
+
email = primary?.email ?? emails[0]?.email ?? null
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!email) {
|
|
80
|
+
return err(authError('No email found on GitHub account'))
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Check allowed emails
|
|
84
|
+
if (config.allowedEmails && !config.allowedEmails.includes(email)) {
|
|
85
|
+
return err(authError(`Email ${email} is not allowed`))
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const user: AuthUser = {
|
|
89
|
+
id: String(userData.id),
|
|
90
|
+
email,
|
|
91
|
+
name: userData.name ?? undefined,
|
|
92
|
+
avatarUrl: userData.avatar_url,
|
|
93
|
+
provider: 'github',
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const session: AuthSession = {
|
|
97
|
+
user,
|
|
98
|
+
expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1000, // 7 days
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
sessions.set(user.id, session)
|
|
102
|
+
return ok(session)
|
|
103
|
+
} catch (error) {
|
|
104
|
+
return err(
|
|
105
|
+
authError(
|
|
106
|
+
`GitHub OAuth failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
107
|
+
),
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
async logout() {
|
|
113
|
+
sessions.clear()
|
|
114
|
+
},
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function generateState(): string {
|
|
119
|
+
const array = new Uint8Array(32)
|
|
120
|
+
crypto.getRandomValues(array)
|
|
121
|
+
return Array.from(array, (b) => b.toString(16).padStart(2, '0')).join('')
|
|
122
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Google } from 'arctic'
|
|
2
|
+
import type { AuthProvider, AuthUser, AuthSession } from '@setzkasten-cms/core'
|
|
3
|
+
import { ok, err, authError } from '@setzkasten-cms/core'
|
|
4
|
+
|
|
5
|
+
export interface GoogleOAuthConfig {
|
|
6
|
+
clientId: string
|
|
7
|
+
clientSecret: string
|
|
8
|
+
redirectUri: string
|
|
9
|
+
allowedEmails?: readonly string[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Google OAuth adapter using Arctic.
|
|
14
|
+
*/
|
|
15
|
+
export function createGoogleAuth(config: GoogleOAuthConfig): AuthProvider {
|
|
16
|
+
const google = new Google(config.clientId, config.clientSecret, config.redirectUri)
|
|
17
|
+
|
|
18
|
+
const sessions = new Map<string, AuthSession>()
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
async getSession() {
|
|
22
|
+
for (const session of sessions.values()) {
|
|
23
|
+
if (session.expiresAt > Date.now()) {
|
|
24
|
+
return ok(session)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return ok(null)
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
getLoginUrl() {
|
|
31
|
+
const state = generateState()
|
|
32
|
+
const codeVerifier = generateState()
|
|
33
|
+
const scopes = ['openid', 'email', 'profile']
|
|
34
|
+
const url = google.createAuthorizationURL(state, codeVerifier, scopes)
|
|
35
|
+
// Store codeVerifier in cookie/session for callback
|
|
36
|
+
return url.toString()
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
async handleCallback(code: string) {
|
|
40
|
+
try {
|
|
41
|
+
// In production, retrieve codeVerifier from cookie/session
|
|
42
|
+
const codeVerifier = '' // TODO: retrieve from session
|
|
43
|
+
const tokens = await google.validateAuthorizationCode(code, codeVerifier)
|
|
44
|
+
const accessToken = tokens.accessToken()
|
|
45
|
+
|
|
46
|
+
// Fetch user info from Google
|
|
47
|
+
const userResponse = await fetch(
|
|
48
|
+
'https://www.googleapis.com/oauth2/v2/userinfo',
|
|
49
|
+
{ headers: { Authorization: `Bearer ${accessToken}` } },
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
if (!userResponse.ok) {
|
|
53
|
+
return err(authError('Failed to fetch Google user info'))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const userData = (await userResponse.json()) as {
|
|
57
|
+
id: string
|
|
58
|
+
email: string
|
|
59
|
+
name: string
|
|
60
|
+
picture: string
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (
|
|
64
|
+
config.allowedEmails &&
|
|
65
|
+
!config.allowedEmails.includes(userData.email)
|
|
66
|
+
) {
|
|
67
|
+
return err(authError(`Email ${userData.email} is not allowed`))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const user: AuthUser = {
|
|
71
|
+
id: userData.id,
|
|
72
|
+
email: userData.email,
|
|
73
|
+
name: userData.name,
|
|
74
|
+
avatarUrl: userData.picture,
|
|
75
|
+
provider: 'google',
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const session: AuthSession = {
|
|
79
|
+
user,
|
|
80
|
+
expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1000,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
sessions.set(user.id, session)
|
|
84
|
+
return ok(session)
|
|
85
|
+
} catch (error) {
|
|
86
|
+
return err(
|
|
87
|
+
authError(
|
|
88
|
+
`Google OAuth failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
89
|
+
),
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
async logout() {
|
|
95
|
+
sessions.clear()
|
|
96
|
+
},
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function generateState(): string {
|
|
101
|
+
const array = new Uint8Array(32)
|
|
102
|
+
crypto.getRandomValues(array)
|
|
103
|
+
return Array.from(array, (b) => b.toString(16).padStart(2, '0')).join('')
|
|
104
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @setzkasten-cms/auth – Authentication adapters
|
|
3
|
+
*
|
|
4
|
+
* MVP: GitHub OAuth + Google OAuth via Arctic
|
|
5
|
+
* Auth determines WHO can access the CMS.
|
|
6
|
+
* Repo commits use a separate GitHub App token.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type { AuthProvider, AuthUser, AuthSession } from '@setzkasten-cms/core'
|
|
10
|
+
|
|
11
|
+
export { createGitHubAuth } from './github-oauth'
|
|
12
|
+
export type { GitHubOAuthConfig } from './github-oauth'
|
|
13
|
+
|
|
14
|
+
export { createGoogleAuth } from './google-oauth'
|
|
15
|
+
export type { GoogleOAuthConfig } from './google-oauth'
|