forked from keycloak/keycloak-nodejs-connect
-
Notifications
You must be signed in to change notification settings - Fork 5
/
index.js
207 lines (207 loc) · 7.22 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
"use strict";
/**
* Created by zhangsong on 2018/8/9.
*/
Object.defineProperty(exports, "__esModule", { value: true });
const bearer_store_1 = require("./stores/bearer-store");
// import CookieStore from './stores/cookie-store';
// import SessionStore from './stores/session-store';
const admin_1 = require("./middleware/admin");
const config_1 = require("./middleware/auth-utils/config");
const grant_manager_1 = require("./middleware/auth-utils/grant-manager");
const grant_attacher_1 = require("./middleware/grant-attacher");
const logout_1 = require("./middleware/logout");
// import PostAuth from './middleware/post-auth';
const protect_1 = require("./middleware/protect");
const setup_1 = require("./middleware/setup");
/**
* Instantiate a Keycloak.
*
* The `config` and `keycloakConfig` hashes are both optional.
*
* The `config` hash, if provided, may include either `store`, pointing
* to the actual session-store used by your application, or `cookies`
* with boolean `true` as the value to support using cookies as your
* authentication store.
*
* A session-based store is recommended, as it allows more assured control
* from the Keycloak console to explicitly logout some or all sessions.
*
* In all cases, also, authentication through a Bearer authentication
* header is supported for non-interactive APIs.
*
* The `keycloakConfig` object, by default, is populated by the contents of
* a `keycloak.json` file installed alongside your application, copied from
* the Keycloak administration console when you provision your application.
*
* @constructor
*
* @param {Object} config Configuration for the Keycloak connector.
* @param {Object} keycloakConfig Keycloak-specific configuration.
*
* @return {Keycloak} A constructed Keycloak object.
*
*/
class Keycloak {
// If keycloakConfig is null, Config() will search for `keycloak.json`.
constructor(config, keycloakConfig) {
this.config = new config_1.default(keycloakConfig); // 读取配置文件
this.grantManager = new grant_manager_1.default(this.config); // 设置
this.stores = [bearer_store_1.default];
if (!config) {
throw new Error('Adapter configuration must be provided.');
}
// Add the custom scope value
this.config.scope = config.scope;
// if (config && config.store && config.cookies) {
// throw new Error('Either `store` or `cookies` may be set, but not both');
// }
if (config && config.store) {
if (Array.isArray(config.store)) {
this.stores.push(...config.store);
}
else {
this.stores.push(config.store);
}
}
// if (config && config.store) {
// this.stores.push(new SessionStore(config.store));
// } else if (config && config.cookies) {
// this.stores.push(CookieStore);
// }
}
/**
* Obtain an array of middleware for use in your application.
*
* Generally this should be installed at the root of your application,
* as it provides general wiring for Keycloak interaction, without actually
* causing Keycloak to get involved with any particular URL until asked
* by using `protect(...)`.
*
* Example:
*
* var app = express();
* var keycloak = new Keycloak();
* app.use( keycloak.middleware() );
*
* Options:
*
* - `logout` URL for logging a user out. Defaults to `/logout`.
* - `admin` Root URL for Keycloak admin callbacks. Defaults to `/`.
*
* @param {Object} options Optional options for specifying details.
*/
middleware(options) {
let option = { logout: '', admin: '' };
if (options) {
option = options;
}
option.logout = option.logout || '/logout';
option.admin = option.admin || '/';
const middlewares = [];
middlewares.push(setup_1.default);
// middlewares.push(PostAuth(this));
middlewares.push(admin_1.default(this, option.admin));
middlewares.push(grant_attacher_1.default(this));
middlewares.push(logout_1.default(this, option.logout));
return middlewares;
}
protect(spec) {
return protect_1.default(this, spec);
}
authenticated(ctx) {
// no-op
}
eauthenticated(ctx) {
// no-op
}
accessDenied(ctx) {
ctx.throw(403, 'Access denied');
}
getGrant(ctx) {
let rawData;
for (const item of this.stores) {
rawData = item.get(ctx);
if (rawData) {
// store = this.stores[i];
break;
}
}
let grantData = rawData;
if (typeof (grantData) === 'string') {
grantData = JSON.parse(grantData);
}
if (grantData && !grantData.error) {
const self = this;
return this.grantManager.createGrant(JSON.stringify(grantData))
.then((grant) => {
self.storeGrant(grant, ctx);
return grant;
})
.catch((e) => {
return Promise.resolve();
});
}
return Promise.resolve();
}
storeGrant(grant, ctx) {
if (this.stores.length < 2 || bearer_store_1.default.get(ctx)) {
// cannot store bearer-only, and should not store if grant is from the
// authorization header
return;
}
if (!grant) {
this.accessDenied(ctx);
return;
}
this.stores[1].wrap(grant);
grant.store(ctx);
return grant;
}
unstoreGrant(sessionId) {
if (this.stores.length < 2) {
// cannot unstore, bearer-only, this is weird
return;
}
this.stores[1].clear(sessionId);
}
getGrantFromCode(code, ctx) {
if (this.stores.length < 2) {
// bearer-only, cannot do this;
throw new Error('Cannot exchange code for grant in bearer-only mode');
}
const sessionId = ctx.session.id;
const self = this;
return this.grantManager.obtainFromCode(ctx, code, sessionId)
.then((grant) => {
self.storeGrant(grant, ctx);
return grant;
});
}
// 组成登录的 URL 重定向过去
loginUrl(uuid, redirectUrl) {
return this.config.realmUrl +
'/protocol/openid-connect/auth' +
'?client_id=' + encodeURIComponent(this.config.clientId) +
'&state=' + encodeURIComponent(uuid) +
'&redirect_uri=' + encodeURIComponent(redirectUrl) +
'&scope=' + encodeURIComponent(this.config.scope ? 'openid ' + this.config.scope : 'openid') +
'&response_type=code';
}
logoutUrl(redirectUrl) {
return this.config.realmUrl +
'/protocol/openid-connect/logout' +
'?redirect_uri=' + encodeURIComponent(redirectUrl);
}
accountUrl() {
return this.config.realmUrl + '/account';
}
getAccount(token) {
return this.grantManager.getAccount(token);
}
redirectToLogin(ctx) {
return !this.config.bearerOnly;
}
}
exports.default = Keycloak;
//# sourceMappingURL=index.js.map