mirror of
https://github.com/wekan/wekan.git
synced 2026-03-12 08:32:33 +01:00
Migrate wekan-oidc from HTTP to fetch
This commit is contained in:
parent
0fcdf47545
commit
49a272d6c0
2 changed files with 118 additions and 92 deletions
|
|
@ -1,4 +1,6 @@
|
||||||
import {addGroupsWithAttributes, addEmail, changeFullname, changeUsername} from './loginHandler';
|
import {addGroupsWithAttributes, addEmail, changeFullname, changeUsername} from './loginHandler';
|
||||||
|
import { fetch, Headers } from 'meteor/fetch';
|
||||||
|
import https from 'https';
|
||||||
|
|
||||||
Oidc = {};
|
Oidc = {};
|
||||||
httpCa = false;
|
httpCa = false;
|
||||||
|
|
@ -18,10 +20,10 @@ var profile = {};
|
||||||
var serviceData = {};
|
var serviceData = {};
|
||||||
var userinfo = {};
|
var userinfo = {};
|
||||||
|
|
||||||
OAuth.registerService('oidc', 2, null, function (query) {
|
OAuth.registerService('oidc', 2, null, async function (query) {
|
||||||
var debug = process.env.DEBUG === 'true';
|
var debug = process.env.DEBUG === 'true';
|
||||||
|
|
||||||
var token = getToken(query);
|
var token = await getToken(query);
|
||||||
if (debug) console.log('XXX: register token:', token);
|
if (debug) console.log('XXX: register token:', token);
|
||||||
|
|
||||||
var accessToken = token.access_token || token.id_token;
|
var accessToken = token.access_token || token.id_token;
|
||||||
|
|
@ -40,7 +42,7 @@ OAuth.registerService('oidc', 2, null, function (query) {
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// normal behaviour, getting the claims from UserInfo endpoint.
|
// normal behaviour, getting the claims from UserInfo endpoint.
|
||||||
userinfo = getUserInfo(accessToken);
|
userinfo = await getUserInfo(accessToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userinfo.ocs) userinfo = userinfo.ocs.data; // Nextcloud hack
|
if (userinfo.ocs) userinfo = userinfo.ocs.data; // Nextcloud hack
|
||||||
|
|
@ -73,7 +75,8 @@ OAuth.registerService('oidc', 2, null, function (query) {
|
||||||
|
|
||||||
if (accessToken) {
|
if (accessToken) {
|
||||||
var tokenContent = getTokenContent(accessToken);
|
var tokenContent = getTokenContent(accessToken);
|
||||||
var fields = _.pick(tokenContent, getConfiguration().idTokenWhitelistFields);
|
var config = await getConfiguration();
|
||||||
|
var fields = _.pick(tokenContent, config.idTokenWhitelistFields);
|
||||||
_.extend(serviceData, fields);
|
_.extend(serviceData, fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -100,7 +103,7 @@ OAuth.registerService('oidc', 2, null, function (query) {
|
||||||
// therefore: keep admin privileges for wekan as before
|
// therefore: keep admin privileges for wekan as before
|
||||||
if(Array.isArray(serviceData.groups) && serviceData.groups.length && typeof serviceData.groups[0] === "string" )
|
if(Array.isArray(serviceData.groups) && serviceData.groups.length && typeof serviceData.groups[0] === "string" )
|
||||||
{
|
{
|
||||||
user = Meteor.users.findOne({'_id': serviceData.id});
|
user = await Meteor.users.findOneAsync({'_id': serviceData.id});
|
||||||
|
|
||||||
serviceData.groups.forEach(function(groupName, i)
|
serviceData.groups.forEach(function(groupName, i)
|
||||||
{
|
{
|
||||||
|
|
@ -119,8 +122,8 @@ OAuth.registerService('oidc', 2, null, function (query) {
|
||||||
|
|
||||||
// Fix OIDC login loop for integer user ID. Thanks to danielkaiser.
|
// Fix OIDC login loop for integer user ID. Thanks to danielkaiser.
|
||||||
// https://github.com/wekan/wekan/issues/4795
|
// https://github.com/wekan/wekan/issues/4795
|
||||||
Meteor.call('groupRoutineOnLogin',serviceData, ""+serviceData.id);
|
await Meteor.callAsync('groupRoutineOnLogin',serviceData, ""+serviceData.id);
|
||||||
Meteor.call('boardRoutineOnLogin',serviceData, ""+serviceData.id);
|
await Meteor.callAsync('boardRoutineOnLogin',serviceData, ""+serviceData.id);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
serviceData: serviceData,
|
serviceData: serviceData,
|
||||||
|
|
@ -134,143 +137,166 @@ if (Meteor.release) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.ORACLE_OIM_ENABLED !== 'true' && process.env.ORACLE_OIM_ENABLED !== true) {
|
if (process.env.ORACLE_OIM_ENABLED !== 'true' && process.env.ORACLE_OIM_ENABLED !== true) {
|
||||||
var getToken = function (query) {
|
var getToken = async function (query) {
|
||||||
var debug = process.env.DEBUG === 'true';
|
var debug = process.env.DEBUG === 'true';
|
||||||
var config = getConfiguration();
|
var config = await getConfiguration();
|
||||||
|
var serverTokenEndpoint;
|
||||||
if(config.tokenEndpoint.includes('https://')){
|
if(config.tokenEndpoint.includes('https://')){
|
||||||
var serverTokenEndpoint = config.tokenEndpoint;
|
serverTokenEndpoint = config.tokenEndpoint;
|
||||||
}else{
|
}else{
|
||||||
var serverTokenEndpoint = config.serverUrl + config.tokenEndpoint;
|
serverTokenEndpoint = config.serverUrl + config.tokenEndpoint;
|
||||||
}
|
}
|
||||||
var requestPermissions = config.requestPermissions;
|
|
||||||
var response;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var postOptions = {
|
var body = new URLSearchParams({
|
||||||
headers: {
|
code: query.code,
|
||||||
Accept: 'application/json',
|
client_id: config.clientId,
|
||||||
"User-Agent": userAgent
|
client_secret: OAuth.openSecret(config.secret),
|
||||||
},
|
redirect_uri: OAuth._redirectUri('oidc', config),
|
||||||
params: {
|
grant_type: 'authorization_code',
|
||||||
code: query.code,
|
state: query.state
|
||||||
client_id: config.clientId,
|
});
|
||||||
client_secret: OAuth.openSecret(config.secret),
|
|
||||||
redirect_uri: OAuth._redirectUri('oidc', config),
|
var fetchOptions = {
|
||||||
grant_type: 'authorization_code',
|
method: 'POST',
|
||||||
state: query.state
|
headers: new Headers({
|
||||||
}
|
'Accept': 'application/json',
|
||||||
};
|
'User-Agent': userAgent,
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
}),
|
||||||
|
body: body.toString()
|
||||||
|
};
|
||||||
|
|
||||||
if (httpCa) {
|
if (httpCa) {
|
||||||
postOptions['npmRequestOptions'] = { ca: httpCa };
|
fetchOptions.agent = new https.Agent({ ca: httpCa });
|
||||||
}
|
}
|
||||||
response = HTTP.post(serverTokenEndpoint, postOptions);
|
|
||||||
|
var response = await fetch(serverTokenEndpoint, fetchOptions);
|
||||||
|
var data = await response.json();
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Failed to get token from OIDC " + serverTokenEndpoint + ": " + response.statusText);
|
||||||
|
}
|
||||||
|
if (data.error) {
|
||||||
|
// if the http response was a json object with an error attribute
|
||||||
|
throw new Error("Failed to complete handshake with OIDC " + serverTokenEndpoint + ": " + data.error);
|
||||||
|
}
|
||||||
|
if (debug) console.log('XXX: getToken response: ', data);
|
||||||
|
return data;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw _.extend(new Error("Failed to get token from OIDC " + serverTokenEndpoint + ": " + err.message),
|
throw _.extend(new Error("Failed to get token from OIDC " + serverTokenEndpoint + ": " + err.message),
|
||||||
{ response: err.response });
|
{ response: err.response });
|
||||||
}
|
}
|
||||||
if (response.data.error) {
|
|
||||||
// if the http response was a json object with an error attribute
|
|
||||||
throw new Error("Failed to complete handshake with OIDC " + serverTokenEndpoint + ": " + response.data.error);
|
|
||||||
} else {
|
|
||||||
if (debug) console.log('XXX: getToken response: ', response.data);
|
|
||||||
return response.data;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.ORACLE_OIM_ENABLED === 'true' || process.env.ORACLE_OIM_ENABLED === true) {
|
if (process.env.ORACLE_OIM_ENABLED === 'true' || process.env.ORACLE_OIM_ENABLED === true) {
|
||||||
|
|
||||||
var getToken = function (query) {
|
var getToken = async function (query) {
|
||||||
var debug = process.env.DEBUG === 'true';
|
var debug = process.env.DEBUG === 'true';
|
||||||
var config = getConfiguration();
|
var config = await getConfiguration();
|
||||||
|
var serverTokenEndpoint;
|
||||||
if(config.tokenEndpoint.includes('https://')){
|
if(config.tokenEndpoint.includes('https://')){
|
||||||
var serverTokenEndpoint = config.tokenEndpoint;
|
serverTokenEndpoint = config.tokenEndpoint;
|
||||||
}else{
|
}else{
|
||||||
var serverTokenEndpoint = config.serverUrl + config.tokenEndpoint;
|
serverTokenEndpoint = config.serverUrl + config.tokenEndpoint;
|
||||||
}
|
}
|
||||||
var requestPermissions = config.requestPermissions;
|
|
||||||
var response;
|
|
||||||
|
|
||||||
// OIM needs basic Authentication token in the header - ClientID + SECRET in base64
|
// OIM needs basic Authentication token in the header - ClientID + SECRET in base64
|
||||||
var dataToken=null;
|
var dataToken = process.env.OAUTH2_CLIENT_ID + ':' + process.env.OAUTH2_SECRET;
|
||||||
var strBasicToken=null;
|
var strBasicToken64 = Buffer.from(dataToken).toString('base64');
|
||||||
var strBasicToken64=null;
|
|
||||||
|
|
||||||
dataToken = process.env.OAUTH2_CLIENT_ID + ':' + process.env.OAUTH2_SECRET;
|
|
||||||
strBasicToken = new Buffer(dataToken);
|
|
||||||
strBasicToken64 = strBasicToken.toString('base64');
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
if (debug) console.log('Basic Token: ', strBasicToken64);
|
if (debug) console.log('Basic Token: ', strBasicToken64);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var postOptions = {
|
var body = new URLSearchParams({
|
||||||
headers: {
|
code: query.code,
|
||||||
Accept: 'application/json',
|
client_id: config.clientId,
|
||||||
"User-Agent": userAgent,
|
client_secret: OAuth.openSecret(config.secret),
|
||||||
"Authorization": "Basic " + strBasicToken64
|
redirect_uri: OAuth._redirectUri('oidc', config),
|
||||||
},
|
grant_type: 'authorization_code',
|
||||||
params: {
|
state: query.state
|
||||||
code: query.code,
|
});
|
||||||
client_id: config.clientId,
|
|
||||||
client_secret: OAuth.openSecret(config.secret),
|
var fetchOptions = {
|
||||||
redirect_uri: OAuth._redirectUri('oidc', config),
|
method: 'POST',
|
||||||
grant_type: 'authorization_code',
|
headers: new Headers({
|
||||||
state: query.state
|
'Accept': 'application/json',
|
||||||
}
|
'User-Agent': userAgent,
|
||||||
};
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
'Authorization': 'Basic ' + strBasicToken64
|
||||||
|
}),
|
||||||
|
body: body.toString()
|
||||||
|
};
|
||||||
|
|
||||||
if (httpCa) {
|
if (httpCa) {
|
||||||
postOptions['npmRequestOptions'] = { ca: httpCa };
|
fetchOptions.agent = new https.Agent({ ca: httpCa });
|
||||||
}
|
}
|
||||||
response = HTTP.post(serverTokenEndpoint, postOptions);
|
|
||||||
|
var response = await fetch(serverTokenEndpoint, fetchOptions);
|
||||||
|
var data = await response.json();
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Failed to get token from OIDC " + serverTokenEndpoint + ": " + response.statusText);
|
||||||
|
}
|
||||||
|
if (data.error) {
|
||||||
|
// if the http response was a json object with an error attribute
|
||||||
|
throw new Error("Failed to complete handshake with OIDC " + serverTokenEndpoint + ": " + data.error);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
if (debug) console.log('XXX: getToken response: ', data);
|
||||||
|
return data;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw _.extend(new Error("Failed to get token from OIDC " + serverTokenEndpoint + ": " + err.message),
|
throw _.extend(new Error("Failed to get token from OIDC " + serverTokenEndpoint + ": " + err.message),
|
||||||
{ response: err.response });
|
{ response: err.response });
|
||||||
}
|
}
|
||||||
if (response.data.error) {
|
|
||||||
// if the http response was a json object with an error attribute
|
|
||||||
throw new Error("Failed to complete handshake with OIDC " + serverTokenEndpoint + ": " + response.data.error);
|
|
||||||
} else {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
if (debug) console.log('XXX: getToken response: ', response.data);
|
|
||||||
return response.data;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var getUserInfo = function (accessToken) {
|
var getUserInfo = async function (accessToken) {
|
||||||
var debug = process.env.DEBUG === 'true';
|
var debug = process.env.DEBUG === 'true';
|
||||||
var config = getConfiguration();
|
var config = await getConfiguration();
|
||||||
// Some userinfo endpoints use a different base URL than the authorization or token endpoints.
|
// Some userinfo endpoints use a different base URL than the authorization or token endpoints.
|
||||||
// This logic allows the end user to override the setting by providing the full URL to userinfo in their config.
|
// This logic allows the end user to override the setting by providing the full URL to userinfo in their config.
|
||||||
|
var serverUserinfoEndpoint;
|
||||||
if (config.userinfoEndpoint.includes("https://")) {
|
if (config.userinfoEndpoint.includes("https://")) {
|
||||||
var serverUserinfoEndpoint = config.userinfoEndpoint;
|
serverUserinfoEndpoint = config.userinfoEndpoint;
|
||||||
} else {
|
} else {
|
||||||
var serverUserinfoEndpoint = config.serverUrl + config.userinfoEndpoint;
|
serverUserinfoEndpoint = config.serverUrl + config.userinfoEndpoint;
|
||||||
}
|
}
|
||||||
var response;
|
|
||||||
try {
|
try {
|
||||||
var getOptions = {
|
var fetchOptions = {
|
||||||
headers: {
|
method: 'GET',
|
||||||
"User-Agent": userAgent,
|
headers: new Headers({
|
||||||
"Authorization": "Bearer " + accessToken
|
'User-Agent': userAgent,
|
||||||
}
|
'Authorization': 'Bearer ' + accessToken
|
||||||
};
|
})
|
||||||
|
};
|
||||||
|
|
||||||
if (httpCa) {
|
if (httpCa) {
|
||||||
getOptions['npmRequestOptions'] = { ca: httpCa };
|
fetchOptions.agent = new https.Agent({ ca: httpCa });
|
||||||
}
|
}
|
||||||
response = HTTP.get(serverUserinfoEndpoint, getOptions);
|
|
||||||
|
var response = await fetch(serverUserinfoEndpoint, fetchOptions);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Failed to fetch userinfo from OIDC " + serverUserinfoEndpoint + ": " + response.statusText);
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = await response.json();
|
||||||
|
if (debug) console.log('XXX: getUserInfo response: ', data);
|
||||||
|
return data;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw _.extend(new Error("Failed to fetch userinfo from OIDC " + serverUserinfoEndpoint + ": " + err.message),
|
throw _.extend(new Error("Failed to fetch userinfo from OIDC " + serverUserinfoEndpoint + ": " + err.message),
|
||||||
{response: err.response});
|
{response: err.response});
|
||||||
}
|
}
|
||||||
if (debug) console.log('XXX: getUserInfo response: ', response.data);
|
|
||||||
return response.data;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var getConfiguration = function () {
|
var getConfiguration = async function () {
|
||||||
var config = ServiceConfiguration.configurations.findOne({ service: 'oidc' });
|
var config = await ServiceConfiguration.configurations.findOneAsync({ service: 'oidc' });
|
||||||
if (!config) {
|
if (!config) {
|
||||||
throw new ServiceConfiguration.ConfigError('Service oidc not configured.');
|
throw new ServiceConfiguration.ConfigError('Service oidc not configured.');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ Package.describe({
|
||||||
Package.onUse(function(api) {
|
Package.onUse(function(api) {
|
||||||
api.use('oauth2', ['client', 'server']);
|
api.use('oauth2', ['client', 'server']);
|
||||||
api.use('oauth', ['client', 'server']);
|
api.use('oauth', ['client', 'server']);
|
||||||
api.use('http', ['server']);
|
api.use('fetch', ['server']);
|
||||||
api.use('underscore', 'client');
|
api.use('underscore', 'client');
|
||||||
api.use('ecmascript');
|
api.use('ecmascript');
|
||||||
api.use('templating', 'client');
|
api.use('templating', 'client');
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue