@cloudron/tegel 1.1.5 → 1.1.6

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.
Files changed (3) hide show
  1. package/index.js +35 -4
  2. package/package.json +2 -2
  3. package/src/oidc.js +10 -0
package/index.js CHANGED
@@ -3,8 +3,7 @@ import fs from 'node:fs';
3
3
  import crypto from 'node:crypto';
4
4
  import session from 'express-session';
5
5
  import FileStoreFactory from 'session-file-store';
6
- import lastMile from '@cloudron/connect-lastmile';
7
- import { HttpError } from '@cloudron/connect-lastmile';
6
+ import { lastMile, HttpError } from '@cloudron/connect-lastmile';
8
7
  import * as oidc from './src/oidc.js';
9
8
 
10
9
  export async function createExpressApp({ oidcConfig, jsonBodySizeLimit = '25mb' }) {
@@ -133,10 +132,35 @@ export function logout(redirectTo) {
133
132
  };
134
133
  }
135
134
 
136
- export function requireAuth(redirectTo = '') {
135
+ export function requireAuth(redirectTo = '', { requiredScopes = [] } = {}) {
137
136
  if (typeof redirectTo !== 'string') throw new Error('requireAuth needs a redirectTo path as non-empty string');
138
137
 
139
- return (req, res, next) => {
138
+ return async (req, res, next) => {
139
+ const accessToken = extractAccessToken(req);
140
+
141
+ if (accessToken) {
142
+ try {
143
+ const introspection = await oidc.introspectToken(accessToken);
144
+
145
+ if (!introspection.active) return next(new HttpError(401, 'Token is not active'));
146
+
147
+ if (requiredScopes.length > 0) {
148
+ const grantedScopes = (introspection.scope || '').split(' ');
149
+ const missingScopes = requiredScopes.filter(s => !grantedScopes.includes(s));
150
+ if (missingScopes.length > 0) return next(new HttpError(403, `Missing required scopes: ${missingScopes.join(', ')}`));
151
+ }
152
+
153
+ req.user = {
154
+ ...introspection,
155
+ username: introspection.sub || introspection.username,
156
+ };
157
+ return next();
158
+ } catch (error) {
159
+ console.error('Token introspection error:', error);
160
+ return next(new HttpError(401, 'Token introspection failed'));
161
+ }
162
+ }
163
+
140
164
  if (req.session && req.session.user) {
141
165
  req.user = req.session.user;
142
166
  return next();
@@ -147,3 +171,10 @@ export function requireAuth(redirectTo = '') {
147
171
  res.redirect(redirectTo);
148
172
  };
149
173
  }
174
+
175
+ function extractAccessToken(req) {
176
+ const authHeader = req.get('Authorization');
177
+ if (authHeader && authHeader.startsWith('Bearer ')) return authHeader.slice(7);
178
+ if (req.query && req.query.accessToken) return req.query.accessToken;
179
+ return null;
180
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudron/tegel",
3
- "version": "1.1.5",
3
+ "version": "1.1.6",
4
4
  "description": "",
5
5
  "license": "GPL-2.0",
6
6
  "author": "Cloudron Developers",
@@ -12,7 +12,7 @@
12
12
  "lint:fix": "eslint . --fix"
13
13
  },
14
14
  "dependencies": {
15
- "@cloudron/connect-lastmile": "^2.3.0",
15
+ "@cloudron/connect-lastmile": "^3.0.0",
16
16
  "express": "^5.2.1",
17
17
  "express-session": "^1.19.0",
18
18
  "openid-client": "^6.8.2",
package/src/oidc.js CHANGED
@@ -88,6 +88,8 @@ export async function handleCallback(req) {
88
88
  );
89
89
  }
90
90
 
91
+ console.log(tokens)
92
+
91
93
  // Clean up session OIDC state
92
94
  delete req.session.oidc;
93
95
 
@@ -103,3 +105,11 @@ export async function handleCallback(req) {
103
105
  user: userInfo,
104
106
  };
105
107
  }
108
+
109
+ /**
110
+ * Introspect an access token via the OIDC provider's token introspection endpoint (RFC 7662)
111
+ */
112
+ export async function introspectToken(token) {
113
+ if (!clientConfig) throw new Error('call initOIDC() first');
114
+ return client.tokenIntrospection(clientConfig, token);
115
+ }