oidc_server.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. import {addGroups, addEmail,changeFullname, changeUsername} from './loginHandler';
  2. Oidc = {};
  3. httpCa = false;
  4. if (process.env.OAUTH2_CA_CERT !== undefined) {
  5. try {
  6. const fs = Npm.require('fs');
  7. if (fs.existsSync(process.env.OAUTH2_CA_CERT)) {
  8. httpCa = fs.readFileSync(process.env.OAUTH2_CA_CERT);
  9. }
  10. } catch(e) {
  11. console.log('WARNING: failed loading: ' + process.env.OAUTH2_CA_CERT);
  12. console.log(e);
  13. }
  14. }
  15. OAuth.registerService('oidc', 2, null, function (query) {
  16. var debug = process.env.DEBUG || false;
  17. console.log(process.env);
  18. var propagateOidcData = process.env.PROPAGATE_OIDC_DATA || false;
  19. var token = getToken(query);
  20. if (debug) console.log('XXX: register token:', token);
  21. var accessToken = token.access_token || token.id_token;
  22. var expiresAt = (+new Date) + (1000 * parseInt(token.expires_in, 10));
  23. var claimsInAccessToken = (process.env.OAUTH2_ADFS_ENABLED === 'true' || process.env.OAUTH2_ADFS_ENABLED === true) || false;
  24. var userinfo;
  25. if(claimsInAccessToken)
  26. {
  27. // hack when using custom claims in the accessToken. On premise ADFS
  28. userinfo = getTokenContent(accessToken);
  29. }
  30. else
  31. {
  32. // normal behaviour, getting the claims from UserInfo endpoint.
  33. userinfo = getUserInfo(accessToken);
  34. }
  35. if (userinfo.ocs) userinfo = userinfo.ocs.data; // Nextcloud hack
  36. if (userinfo.metadata) userinfo = userinfo.metadata // Openshift hack
  37. if (debug) console.log('XXX: userinfo:', userinfo);
  38. var serviceData = {};
  39. serviceData.id = userinfo[process.env.OAUTH2_ID_MAP]; // || userinfo["id"];
  40. serviceData.username = userinfo[process.env.OAUTH2_USERNAME_MAP]; // || userinfo["uid"];
  41. serviceData.fullname = userinfo[process.env.OAUTH2_FULLNAME_MAP]; // || userinfo["displayName"];
  42. serviceData.accessToken = accessToken;
  43. serviceData.expiresAt = expiresAt;
  44. // If on Oracle OIM email is empty or null, get info from username
  45. if (process.env.ORACLE_OIM_ENABLED === 'true' || process.env.ORACLE_OIM_ENABLED === true) {
  46. if (userinfo[process.env.OAUTH2_EMAIL_MAP]) {
  47. serviceData.email = userinfo[process.env.OAUTH2_EMAIL_MAP];
  48. } else {
  49. serviceData.email = userinfo[process.env.OAUTH2_USERNAME_MAP];
  50. }
  51. }
  52. if (process.env.ORACLE_OIM_ENABLED !== 'true' && process.env.ORACLE_OIM_ENABLED !== true) {
  53. serviceData.email = userinfo[process.env.OAUTH2_EMAIL_MAP]; // || userinfo["email"];
  54. }
  55. if (accessToken) {
  56. var tokenContent = getTokenContent(accessToken);
  57. var fields = _.pick(tokenContent, getConfiguration().idTokenWhitelistFields);
  58. _.extend(serviceData, fields);
  59. }
  60. if (token.refresh_token)
  61. serviceData.refreshToken = token.refresh_token;
  62. if (debug) console.log('XXX: serviceData:', serviceData);
  63. var profile = {};
  64. profile.name = userinfo[process.env.OAUTH2_FULLNAME_MAP]; // || userinfo["displayName"];
  65. profile.email = userinfo[process.env.OAUTH2_EMAIL_MAP]; // || userinfo["email"];
  66. if (propagateOidcData)
  67. {
  68. users= Meteor.users;
  69. user = users.findOne({'services.oidc.id': serviceData.id});
  70. if(user)
  71. {
  72. serviceData.groups = profile.groups
  73. profile.groups = userinfo["groups"];
  74. if(userinfo["groups"]) addGroups(user, userinfo["groups"]);
  75. if(profile.email) addEmail(user, profile.email)
  76. if(profile.name) changeFullname(user, profile.name)
  77. if(profile.username) changeUsername(user, profile.username)
  78. }
  79. }
  80. if (debug) console.log('XXX: profile:', profile);
  81. return {
  82. serviceData: serviceData,
  83. options: { profile: profile }
  84. };
  85. });
  86. var userAgent = "Meteor";
  87. if (Meteor.release) {
  88. userAgent += "/" + Meteor.release;
  89. }
  90. if (process.env.ORACLE_OIM_ENABLED !== 'true' && process.env.ORACLE_OIM_ENABLED !== true) {
  91. var getToken = function (query) {
  92. var debug = process.env.DEBUG || false;
  93. var config = getConfiguration();
  94. if(config.tokenEndpoint.includes('https://')){
  95. var serverTokenEndpoint = config.tokenEndpoint;
  96. }else{
  97. var serverTokenEndpoint = config.serverUrl + config.tokenEndpoint;
  98. }
  99. var requestPermissions = config.requestPermissions;
  100. var response;
  101. try {
  102. var postOptions = {
  103. headers: {
  104. Accept: 'application/json',
  105. "User-Agent": userAgent
  106. },
  107. params: {
  108. code: query.code,
  109. client_id: config.clientId,
  110. client_secret: OAuth.openSecret(config.secret),
  111. redirect_uri: OAuth._redirectUri('oidc', config),
  112. grant_type: 'authorization_code',
  113. state: query.state
  114. }
  115. };
  116. if (httpCa) {
  117. postOptions['npmRequestOptions'] = { ca: httpCa };
  118. }
  119. response = HTTP.post(serverTokenEndpoint, postOptions);
  120. } catch (err) {
  121. throw _.extend(new Error("Failed to get token from OIDC " + serverTokenEndpoint + ": " + err.message),
  122. { response: err.response });
  123. }
  124. if (response.data.error) {
  125. // if the http response was a json object with an error attribute
  126. throw new Error("Failed to complete handshake with OIDC " + serverTokenEndpoint + ": " + response.data.error);
  127. } else {
  128. if (debug) console.log('XXX: getToken response: ', response.data);
  129. return response.data;
  130. }
  131. };
  132. }
  133. if (process.env.ORACLE_OIM_ENABLED === 'true' || process.env.ORACLE_OIM_ENABLED === true) {
  134. var getToken = function (query) {
  135. var debug = (process.env.DEBUG === 'true' || process.env.DEBUG === true) || false;
  136. var config = getConfiguration();
  137. if(config.tokenEndpoint.includes('https://')){
  138. var serverTokenEndpoint = config.tokenEndpoint;
  139. }else{
  140. var serverTokenEndpoint = config.serverUrl + config.tokenEndpoint;
  141. }
  142. var requestPermissions = config.requestPermissions;
  143. var response;
  144. // OIM needs basic Authentication token in the header - ClientID + SECRET in base64
  145. var dataToken=null;
  146. var strBasicToken=null;
  147. var strBasicToken64=null;
  148. dataToken = process.env.OAUTH2_CLIENT_ID + ':' + process.env.OAUTH2_SECRET;
  149. strBasicToken = new Buffer(dataToken);
  150. strBasicToken64 = strBasicToken.toString('base64');
  151. // eslint-disable-next-line no-console
  152. if (debug) console.log('Basic Token: ', strBasicToken64);
  153. try {
  154. var postOptions = {
  155. headers: {
  156. Accept: 'application/json',
  157. "User-Agent": userAgent,
  158. "Authorization": "Basic " + strBasicToken64
  159. },
  160. params: {
  161. code: query.code,
  162. client_id: config.clientId,
  163. client_secret: OAuth.openSecret(config.secret),
  164. redirect_uri: OAuth._redirectUri('oidc', config),
  165. grant_type: 'authorization_code',
  166. state: query.state
  167. }
  168. };
  169. if (httpCa) {
  170. postOptions['npmRequestOptions'] = { ca: httpCa };
  171. }
  172. response = HTTP.post(serverTokenEndpoint, postOptions);
  173. } catch (err) {
  174. throw _.extend(new Error("Failed to get token from OIDC " + serverTokenEndpoint + ": " + err.message),
  175. { response: err.response });
  176. }
  177. if (response.data.error) {
  178. // if the http response was a json object with an error attribute
  179. throw new Error("Failed to complete handshake with OIDC " + serverTokenEndpoint + ": " + response.data.error);
  180. } else {
  181. // eslint-disable-next-line no-console
  182. if (debug) console.log('XXX: getToken response: ', response.data);
  183. return response.data;
  184. }
  185. };
  186. }
  187. var getUserInfo = function (accessToken) {
  188. var debug = process.env.DEBUG || false;
  189. var config = getConfiguration();
  190. // Some userinfo endpoints use a different base URL than the authorization or token endpoints.
  191. // This logic allows the end user to override the setting by providing the full URL to userinfo in their config.
  192. if (config.userinfoEndpoint.includes("https://")) {
  193. var serverUserinfoEndpoint = config.userinfoEndpoint;
  194. } else {
  195. var serverUserinfoEndpoint = config.serverUrl + config.userinfoEndpoint;
  196. }
  197. var response;
  198. try {
  199. var getOptions = {
  200. headers: {
  201. "User-Agent": userAgent,
  202. "Authorization": "Bearer " + accessToken
  203. }
  204. };
  205. if (httpCa) {
  206. getOptions['npmRequestOptions'] = { ca: httpCa };
  207. }
  208. response = HTTP.get(serverUserinfoEndpoint, getOptions);
  209. } catch (err) {
  210. throw _.extend(new Error("Failed to fetch userinfo from OIDC " + serverUserinfoEndpoint + ": " + err.message),
  211. {response: err.response});
  212. }
  213. if (debug) console.log('XXX: getUserInfo response: ', response.data);
  214. return response.data;
  215. };
  216. var getConfiguration = function () {
  217. var config = ServiceConfiguration.configurations.findOne({ service: 'oidc' });
  218. if (!config) {
  219. throw new ServiceConfiguration.ConfigError('Service oidc not configured.');
  220. }
  221. return config;
  222. };
  223. var getTokenContent = function (token) {
  224. var content = null;
  225. if (token) {
  226. try {
  227. var parts = token.split('.');
  228. var header = JSON.parse(Buffer.from(parts[0], 'base64').toString());
  229. content = JSON.parse(Buffer.from(parts[1], 'base64').toString());
  230. var signature = Buffer.from(parts[2], 'base64');
  231. var signed = parts[0] + '.' + parts[1];
  232. } catch (err) {
  233. this.content = {
  234. exp: 0
  235. };
  236. }
  237. }
  238. return content;
  239. }
  240. Oidc.retrieveCredential = function (credentialToken, credentialSecret) {
  241. return OAuth.retrieveCredential(credentialToken, credentialSecret);
  242. };