@moteurio/plugin-auth-github 0.0.0-stage → 2026.10.23

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.
@@ -0,0 +1,20 @@
1
+ import type { PluginManifest, PluginRouteContext, PluginRouteContribution } from '@moteurio/types/Plugin.js';
2
+ import { type PluginInitContext as PluginContext } from '@moteurio/plugin-sdk';
3
+ export declare const manifest: PluginManifest;
4
+ export declare function init(_ctx: PluginContext): void;
5
+ export declare function getRoutes(ctx: PluginRouteContext): PluginRouteContribution;
6
+ export declare function getAuthProviders(): {
7
+ id: string;
8
+ label: string;
9
+ enabled: boolean;
10
+ loginPath: string;
11
+ callbackPath: string;
12
+ }[];
13
+ declare const _default: {
14
+ manifest: PluginManifest;
15
+ init: typeof init;
16
+ getRoutes: typeof getRoutes;
17
+ getAuthProviders: typeof getAuthProviders;
18
+ };
19
+ export default _default;
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAC7G,OAAO,EAMH,KAAK,iBAAiB,IAAI,aAAa,EAC1C,MAAM,sBAAsB,CAAC;AAM9B,eAAO,MAAM,QAAQ,EAAE,cAOtB,CAAC;AAEF,wBAAgB,IAAI,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAAG;AAiGlD,wBAAgB,SAAS,CAAC,GAAG,EAAE,kBAAkB,GAAG,uBAAuB,CAM1E;AAED,wBAAgB,gBAAgB;;;;;;IAU/B;;;;;;;AAED,wBAA+D"}
package/dist/index.js ADDED
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Optional plugin: GitHub OAuth. Registers GET /auth/github and GET /auth/github/callback.
3
+ */
4
+ import { Router } from 'express';
5
+ import axios from 'axios';
6
+ import { beginOAuth, completeOAuth, oauthConfig, redirectAfterLogin, redirectLoginFailed } from '@moteurio/plugin-sdk';
7
+ import { loginWithProvider } from '@moteurio/core/auth.js';
8
+ const NOT_OFFERED = { error: 'GitHub sign-in is not available on this server.' };
9
+ export const manifest = {
10
+ id: 'auth-github',
11
+ label: 'GitHub OAuth',
12
+ description: 'Sign in with GitHub',
13
+ source: 'opensource',
14
+ kind: 'optional',
15
+ scope: 'global'
16
+ };
17
+ export function init(_ctx) { }
18
+ function createGitHubRouter(ctx) {
19
+ const router = Router();
20
+ router.get('/github', (req, res) => {
21
+ const config = oauthConfig('github');
22
+ if (!config) {
23
+ res.status(404).json(NOT_OFFERED);
24
+ return;
25
+ }
26
+ const params = new URLSearchParams({
27
+ client_id: config.clientId,
28
+ redirect_uri: config.redirectUri,
29
+ scope: 'read:user user:email',
30
+ state: beginOAuth(req, res, 'github')
31
+ });
32
+ res.redirect(`https://github.com/login/oauth/authorize?${params.toString()}`);
33
+ });
34
+ router.get('/github/callback', async (req, res) => {
35
+ const config = oauthConfig('github');
36
+ if (!config) {
37
+ res.status(404).json(NOT_OFFERED);
38
+ return;
39
+ }
40
+ const started = completeOAuth(req, res, 'github');
41
+ if (!started)
42
+ return redirectLoginFailed(res, 'state');
43
+ const code = req.query.code;
44
+ if (typeof code !== 'string' || !code)
45
+ return redirectLoginFailed(res, 'failed');
46
+ try {
47
+ const tokenRes = await axios.post('https://github.com/login/oauth/access_token', {
48
+ client_id: config.clientId,
49
+ client_secret: config.clientSecret,
50
+ code,
51
+ redirect_uri: config.redirectUri
52
+ }, { headers: { Accept: 'application/json' } });
53
+ const accessToken = tokenRes.data.access_token;
54
+ if (!accessToken)
55
+ return redirectLoginFailed(res, 'failed');
56
+ const headers = { Authorization: `Bearer ${accessToken}` };
57
+ const [userRes, emailsRes] = await Promise.all([
58
+ axios.get('https://api.github.com/user', { headers }),
59
+ axios.get('https://api.github.com/user/emails', { headers })
60
+ ]);
61
+ const email = emailsRes.data.find((e) => e.primary && e.verified)?.email;
62
+ if (!email)
63
+ return redirectLoginFailed(res, 'email');
64
+ const profile = userRes.data;
65
+ const { token } = await loginWithProvider({ auth: { githubId: profile.id }, email, name: profile.name || profile.login }, ctx.authCallbacks?.runOnboardingForNewUser);
66
+ redirectAfterLogin(res, token, started.next);
67
+ }
68
+ catch (err) {
69
+ if (err instanceof Error && err.message === 'User is not active') {
70
+ return redirectLoginFailed(res, 'inactive');
71
+ }
72
+ console.error('GitHub callback error:', err);
73
+ redirectLoginFailed(res, 'failed');
74
+ }
75
+ });
76
+ return router;
77
+ }
78
+ const openapi = {
79
+ '/auth/github': {
80
+ get: {
81
+ summary: 'Redirect to GitHub login',
82
+ tags: ['Auth'],
83
+ description: "Redirects the user to GitHub's OAuth login screen. `next` (a path in the app) is where they land after signing in.",
84
+ parameters: [{ name: 'next', in: 'query', required: false, schema: { type: 'string' } }],
85
+ responses: { '302': { description: 'Redirect to GitHub OAuth screen' } }
86
+ }
87
+ },
88
+ '/auth/github/callback': {
89
+ get: {
90
+ summary: 'GitHub OAuth callback',
91
+ tags: ['Auth'],
92
+ description: 'Checks `state` against the browser that started the sign-in, creates or reuses the account with the verified primary email, then redirects to `AUTH_REDIRECT_AFTER_LOGIN` with `#token=…&next=…`, or `#error=state|email|inactive|failed`.',
93
+ parameters: [
94
+ { name: 'code', in: 'query', required: true, schema: { type: 'string' } },
95
+ { name: 'state', in: 'query', required: true, schema: { type: 'string' } }
96
+ ],
97
+ responses: { '302': { description: 'Redirect to the app with a token or an error in the fragment' } }
98
+ }
99
+ }
100
+ };
101
+ export function getRoutes(ctx) {
102
+ return {
103
+ path: '/auth',
104
+ router: createGitHubRouter(ctx),
105
+ openapi: { paths: openapi }
106
+ };
107
+ }
108
+ export function getAuthProviders() {
109
+ return [
110
+ {
111
+ id: 'github',
112
+ label: 'GitHub',
113
+ enabled: oauthConfig('github') !== null,
114
+ loginPath: '/auth/github',
115
+ callbackPath: '/auth/github/callback'
116
+ }
117
+ ];
118
+ }
119
+ export default { manifest, init, getRoutes, getAuthProviders };
package/package.json CHANGED
@@ -1,6 +1,34 @@
1
1
  {
2
- "name": "@moteurio/plugin-auth-github",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
2
+ "name": "@moteurio/plugin-auth-github",
3
+ "version": "2026.10.23",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": "./dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc -b",
15
+ "clean": "rimraf dist",
16
+ "prepublishOnly": "pnpm run build",
17
+ "test": "vitest run --passWithNoTests"
18
+ },
19
+ "dependencies": {
20
+ "@moteurio/core": "^2026.10.23",
21
+ "@moteurio/plugin-sdk": "^2026.10.23",
22
+ "@moteurio/types": "^2026.10.23",
23
+ "axios": "^1.13.6",
24
+ "express": "^4.19.2",
25
+ "openapi-types": "^12.1.3"
26
+ },
27
+ "devDependencies": {
28
+ "@types/express": "^5.0.3"
29
+ },
30
+ "license": "MIT",
31
+ "publishConfig": {
32
+ "access": "public"
33
+ }
6
34
  }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.