RocketChat Oauth flow
RocketChat Oauth overviewβ
RocketChatServer Oauth configurationβ
To create a custom authentication method in your Rocket.Chat workspace:
-
Navigate to Administration > Workspace > Settings > OAuth.
-
Click on Custom OAuth and set the new OAuth integration name.
-
After creating the new integration, you are given the Callback URL. Provide this as the redirect URI when you are creating your Azure app.
-
You will also need to fill in other configuration fields.
For more detialed info please see the Auth with Microsoft code flow
Home page request and responseβ
Rocket.Chat offers integrations with various providers such as Google, Apple, and GitHub. This provides flexibility and security so users can log in with any of these credentials. All the type of login method will loaded in the home page. Authorize path, Redirect uri, Response type and other import loaded info which is very useful for login.
Generate credentialToken and request auth codeβ
Generate credentialTokenβ
After click the Oauth login button, the credentialToken which like session id for login will generated in the random way, so only the user and current client(browser) know the session id. Then the loginUrl composed with authorizePath, redirect_uri, response_type and state will be as request to author server.
The credentialToken and state is key info for login. The state is base64 type which encoded from loginStyle, credentialToken, and redirectUrl. And the state is import query parameter to redirect back to RocketServer. It will be used to retry the credentialToken and credentialSecret info for login. There are two type of loign style redirect and popup.
OAuth._stateParam = (loginStyle, credentialToken, redirectUrl) => {
const state = {
loginStyle,
credentialToken,
isCordova: Meteor.isCordova
};
if (loginStyle === 'redirect' ||
(Meteor.settings?.public?.packages?.oauth?.setRedirectUrlWhenLoginStyleIsPopup && loginStyle === 'popup')
) {
state.redirectUrl = redirectUrl || ('' + window.location);
}
return Base64.encode(JSON.stringify(state));
};
async requestCredential(
options: Meteor.LoginWithExternalServiceOptions = {},
credentialRequestCompleteCallback: (credentialTokenOrError?: string | Error) => void,
) {
const config = await loginServices.loadLoginService<OAuthConfiguration>(this.name);
if (!config) {
if (credentialRequestCompleteCallback) {
credentialRequestCompleteCallback(new Accounts.ConfigError());
}
return;
}
const credentialToken = Random.secret();
const loginStyle = OAuth._loginStyle(this.name, config);
const separator = this.authorizePath.indexOf('?') !== -1 ? '&' : '?';
const loginUrl =
`${this.authorizePath}${separator}client_id=${config.clientId}&redirect_uri=${encodeURIComponent(
OAuth._redirectUri(this.name, config),
)}&response_type=${encodeURIComponent(this.responseType)}` +
`&state=${encodeURIComponent(OAuth._stateParam(loginStyle, credentialToken, options.redirectUrl))}&scope=${encodeURIComponent(
this.scope,
)}`;
OAuth.launchLogin({
loginService: this.name,
loginStyle,
loginUrl,
credentialRequestCompleteCallback,
credentialToken,
popupOptions: {
width: 900,
height: 450,
},
});
}
https://login.microsoftonline.com/d5e685a9-3e23-42d9-9efa-15cc5c1ce7db/oauth2/authorize?
client_id=e3845613-0331-49c0-9f11-fb6a634242d2&
redirect_uri=https%3A%2F%2F212c-34-92-204-228.ngrok-free.app%2F_oauth%2F365oauth&
response_type=code&
state=eyJsb2dpblN0eWxlIjoicmVkaXJlY3Q1LCJjcmVkZW50aWFsV39rZW4iOiJyMHk4Vk54ZlN4MUhmY0lFZXVLbnQ1dnAzNW1aWHM2cHdWTkhzcWRNb1BvIiwiaXNDb3Jkb3ZhIjpmYWxzZSwicmVkaXJlY3RVcmwiOiJodHRwczovLzIxMmMtMzQtOTItMjA0LTIyOC5uf3Jvay1gcmVlLmFwcC9ob21lIn0%3D&
scope=openid%20profile%20email
Request auth codeβ
The auth code will be the response of LoginUrl request.
code=0.AbcAqYXm1SM-2UKe-hXMXBzn2x4WhOMxA8BJnxH7amNCQtL8APU.AgABBAIAAAApTwJmzXqdR4BN2miheQMYAgDs_wUA9P8Hjlb9sDQamfLxWl_ukGw1Js8IBAlNSq9K3y6MFvkr-uCDOasVt1WLipwEY2irrnDPJVI78EONQ8P1jdKlIDjt-5sQME63u654FbJGKq8H-AV6YrDtaYxfV7cC0YGEcVlbDwYzT0-NRlHW17I9OXQmF0wyg0FIPqK8EJa-zCKPkGTGwqsPh11Ul5-qv90TxMjsfZPd9KZs2Kixiigtx_otW_S6qhbFHCXQVeSG_sO9W0Vn2avdOiAktEILUOWQo-ZH-wmSxfEzibkjvCZYXEtccP1kDxUhQa5Ck4zQWReIjqf2atH3_74ZhFFGMReKSVujqfO46hA7gd4OY7VVGikGlloUWVSFboMXQvQ1GKi7EJ2DEOiIXVM3Uu4_sl3X4dKShcBjSy4kuHfb8MMpQuUhL4NFmBg7A-qVl4D1muhayPaJyuT2-prggC5ciL7rlgMAWcoGgU5dpC-WujQRQVbva1w1HyGI6QL8biiU_fP0wP6-0mAsNVsBDhuMtoh0VZ2eKbLWGyvLL0pgTh8XiUMte9t5JgL4dSzlVAcHXUK2HWBeHql2J0LsR73arQHMh5QhlRa94JGB1zNtFnXn5xjfvkYDF3zTuxIwJa4JoEtPu9Krnu6-rhA3sHVUNUE6uTBDoIz5udVmAyaa4hNwDEhajSk4qCvEvEgk&
Return code and redirect_uri for callbackβ
Because the LoginUrl contain the redirect_uri as parameter, the Oauth2 Authorize server will return redirect_uri and code for callback to RocketChatServer. In the Oauth2 Authorize server, redirect_uri will be checked weather it is equal to one of the uris in the server.
Redirect to redirect_uri with code and stateβ
Redirect means to access the redirect_uri is not from RokcetChatClient directly. It is a passive behavior on the browser side. After getting the code info and loading new page from the LoginUrl, the page will direct to redirect_uri automatically, so this request is not issued from RokcetChatClient, but passive behavior on the browser.
Return credentialToken and credentialSecretβ
In the following, the redirect_uri with code, state, and session_state as parameter to obtain credentialToken and credentialSecret from RocketChatServer. The credentialToken and credentialSecret is parameter for login.
https://212c-34-92-204-228.ngrok-free.app/_oauth/365oauth?
code=0.AbcAqYXm1SM-2UKe-hXMXBzn2x4WhOMxA8BJnxH7amNCQtL8APU.AgABBAIAAAApTwJmzXqdR4BN2miheQMYAgDs_wUA9P8Hjlb9sDQamfLxWl_ukGw1Js8IBAlNSq9K3y6MFvkr-uCDOasVt1WLipwEY2irrnDPJVI78EONQ8P1jdKlIDjt-5sQME63u654FbJGKq8H-AV6YrDtaYxfV7cC0YGEcVlbDwYzT0-NRlHW17I9OXQmF0wyg0FIPqK8EJa-zCKPkGTGwqsPh11Ul5-qv90TxMjsfZPd9KZs2Kixiigtx_otW_S6qhbFHCXQVeSG_sO9W0Vn2avdOiAktEILUOWQo-ZH-wmSxfEzibkjvCZYXEtccP1kDxUhQa5Ck4zQWReIjqf2atH3_74ZhFFGMReKSVujqfO46hA7gd4OY7VVGikGlloUWVSFboMXQvQ1GKi7EJ2DEOiIXVM3Uu4_sl3X4dKShcBjSy4kuHfb8MMpQuUhL4NFmBg7A-qVl4D1muhayPaJyuT2-prggC5ciL7rlgMAWcoGgU5dpC-WujQRQVbva1w1HyGI6QL8biiU_fP0wP6-0mAsNVsBDhuMtoh0VZ2eKbLWGyvLL0pgTh8XiUMte9t5JgL4dSzlVAcHXUK2HWBeHql2J0LsR73arQHMh5QhlRa94JGB1zNtFnXn5xjfvkYDF3zTuxIwJa4JoEtPu9Krnu6-rhA3sHVUNUE6uTBDoIz5udVmAyaa4hNwDEhajSk4qCvEvEgk&
state=eyJsb2dpblN0eWxlIjoicmVkaXWERY3QiLCJjcmVkZW50aWFsVG9rZW4iOiJwVjNOcHdRUnlLc21HbUxwc3EtRHlHWjRmeGVwYWl6TXhPRWZmQVNiWmVPIiwiaXNDb3Jkb3ZhIjpmYWxzZSwicmVkaXJlY3RVcmwiOiJodHRwczovLzIxMmMtMzQtOTItMjA0LTIyOC5uZ3Jvay1mcmVlLmFwcC9ob21lIn0%3d&
session_state=2e1ade24-bffe-46ef-b1c7-934dc8dfbd00#
Request Oauth token by authorization codeβ
The code, state, and session_state will be the query parameter in the registerService. In this funciton, the RocketServer will getAccessToken with code then getIdentity with access_token. So after registerService function execute, the RocketServer will have all the infomation about indentity include name, email, access_token, refres_token and the others. Let bread down step by step in the .
custom_oauth_server registerService query 05 {
code: '0.AbcAqYXm1SM-2UKe-hXMXBzn2xNWhOMxA8BJnxH7amNCQtL8APU.AgABBAIAAAApTwJmzXqdR4BN2miheQMYAgDs_wUA9P9DwxS3im8tRSDQdtHxq88XUJxfHdm8qXE_sPaboChfsUSBiWTOOWk5wi3gGT_zILXoafj7-Ae9DUsG9gBPYFk4HXNykWKEhJWyHGeXjR1LVwyZV16w9udhzhZFYpSaTg-jEPcv5sFzbgoxviDNcxcIQTzhmrV2C-SfBCV4-qOZJ4lIcP6eAFm1AexMrnpO5fnI2k-mnZS43E3PiNIQsqPQrItEx1miMNxqAshwQ1QIJaCnzUtus6zd4d6cZma08zQy65wn0axWE2TEXleqOoV81t1-hapdyd-ROpobuYk6ImKWI2Aw3hnjJqWCfJvgkxDLk0ycFuRcdUZRUug8DQtypJk7lrG13NJ7Cx2dbAWNTxLT4kwnvYGjLSLqTk9YQCdwmFgcYsGLk4cqlHTCIg4BJ4ETUxOjYlk2TVmD6vgA3MOZSMREwqyyh4s8iZg9bC2qGQkRL26FNlaiBHRMOGMmptBDo7EONp5RGRMz5A2Wnzepx4w3YRtJUfgYdALRNM6d4lh0l22YUyuFja6HFIS4QIRrHjbOHXyIC2w1_nCfcBPfpw4h75Fmf2iJFvmBeRNdlmU3BMCCnZ2rghkfTSohqzwe2HgBNGKo6pmW6o8WONTDnsIaWtEB4aPsBqL2NX5rOEnQMF2yBsNh_KMhziGjJkQy4ikl1mkAFQ',
state: 'eyJsb2dpblN0eWxlIjoicmVkaXJlY3QiLCJjcmVkZW50aWFsVG9rZW4iOiJBNnpMMXZEQnNaTkZMTGw0YzE4bW5YQXJsTU9MUU1UMDJyWXpWbkxfcGp0IiwiaXNDb3Jkb3ZhIjpmYWxzZSwicmVkaXJlY3RVcmwiOiJodHRwczovLzIxMmMtMzQtOTItMjA0LTIyOC5uZ3Jvay1mcmVlLmFwcC9ob21lIn0=',
session_state: '2e1ade24-bffe-46ef-b1c7-934dc8dfbd00'
}
registerService() {
const self = this;
OAuth.registerService(this.name, 2, null, async (query) => {
console.log("XXXXXXXXXXXXXXXXXXXx custom_oauth_server registerService query 05", query);
const response = await self.getAccessToken(query);
const identity = await self.getIdentity(response.access_token, query);
const serviceData = {
_OAuthCustom: true,
serverURL: self.serverURL,
accessToken: response.access_token,
idToken: response.id_token,
expiresAt: +new Date() + 1000 * parseInt(response.expires_in, 10),
};
// only set the token in serviceData if it's there. this ensures
// that we don't lose old ones (since we only get this on the first
// log in attempt)
if (response.refresh_token) {
serviceData.refreshToken = response.refresh_token;
}
_.extend(serviceData, identity);
const data = {
serviceData,
options: {
profile: {
name: identity.name,
},
},
};
//logger.debug({ msg: 'RegisterService Data', response });
console.log("XXXXXXXXXXXXXXXXXXXx custom_oauth_server registerService data 05", data);
return data;
});
}
The RocketChat can use the authorization code to request an access token for the target resource. Authorization codes are short lived. Typically, they expire after about 10 minutes.
async getAccessToken(query) {
const config = await ServiceConfiguration.configurations.findOneAsync({ service: this.name });
if (!config) {
throw new Accounts.ConfigError();
}
let response = undefined;
const headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': this.userAgent, // http://doc.gitlab.com/ce/api/users.html#Current-user
'Accept': 'application/json',
};
const params = new URLSearchParams({
code: query.code,
redirect_uri: OAuth._redirectUri(this.name, config),
grant_type: 'authorization_code',
state: query.state,
});
// Only send clientID / secret once on header or payload.
if (this.tokenSentVia === 'header') {
const b64 = Buffer.from(`${config.clientId}:${OAuth.openSecret(config.secret)}`).toString('base64');
headers.Authorization = `Basic ${b64}`;
} else {
params.append('client_secret', config.secret);
params.append('client_id', config.clientId);
}
try {
const request = await fetch(`${this.tokenPath}`, {
method: 'POST',
headers,
body: params,
});
if (!request.ok) {
throw new Error(request.statusText);
}
response = await request.json();
console.log("XXXXXXXXXXXXXXXXXXXx custom_oauth_server getAccessToken 03", response);
} catch (err) {
const error = new Error(`Failed to complete OAuth handshake with ${this.name} at ${this.tokenPath}. ${err.message}`);
throw _.extend(error, { response: err.response });
}
if (response.error) {
// if the http response was a json object with an error attribute
throw new Error(`Failed to complete OAuth handshake with ${this.name} at ${this.tokenPath}. ${response.error}`);
} else {
return response;
}
}
Return an access_token and a refresh_tokenβ
custom_oauth_server getAccessToken 03 {
token_type: 'Bearer',
expires_in: '3599',
ext_expires_in: '3599',
expires_on: '1719714834',
access_token: 'PAQABAQIAAAApTwJmzXqdR4BN2miheQMYlbd5ZfbyIQUX1iW0FWGmFGvyjTFuy2zHZ1PoDvLgz0D6hAas62MopvXHFIhX53amdiH8_8t-SAGJsjr--GeBeY4Xa3CBx4-1Wg6KutRkg6nZPb-tOO-JSvdlEwlJ_QqPbv_MKZjoqiDbbK0AwbeiQmQm7uTxfk_EjiXxJGM5nYGoHNeg6rslqYYROsjEw2mFf3kyYYFEbUstPfc6Mc_4t3I8QJTNfC0NELnnNHP1b3khDyXtWEeYgcG4v9bNu3_s8MuUJmF1Cv1oWG8p7uTAmpmt84aQ5lnkO3fcxH4yLOH_CPcYLa8JU4K6EaqW7qq8rlvFCTQk0EzWDfhDjfSXESY8zylhFyOqRKsuBVlUiQ7FU42ukPbNDtdJEcwgOm1m9KIFEU5EDUB7ljyPwF_nwgXHnhF6tsU0XEFR3hCAsp0_TgGvtE5xR7mNHC59ZrY86QpcgTHC3lXyDj58VGgZTiAA',
refresh_token: '0.AbcAqYXm1SM-2UKe-hXMXBzn2xNWhOMxA8BJnxH7amNCQtL8APU.AgABAwEAAAApTwJmzXqdR4BN2miheQMYAgDs_wUA9P_RBLQedJRbR8Ll3VPCsjY6zuGnnG6AaZjrFXJGr7hxvjrMD8Azvdxc2BPjThB6qx9JVac8azajaKio_azzrov5EgHgPxCoX5tIL4mV-Yg98cVi5Xm8kOrCjUupc6l0qXRec5Eq69VHJwIUyJrsxpPc8LsHYwDNoKhPWm7fBVKUW0g5ifxg_RBAF43MJYrC5z65tgBRmXwsRJ7ZgDBqnXelYWLKgd-3HG5MkuRpv3JB8vxGRSS5kKdEExU0VRXJ08MMVNHIX_bdzFKyf8LYB2V1sU7gxnWnTmDgQAwvS0Asp0XE-nPY9JtUs_NkgsWD_JQEXv40ib3kOXWNf07ZnVD0DC4E-qE2G76CoEq0l0OWjH9EHf_xhLr21skTWRnaRnnQ8UN1dpyNrf5ymSkhxvTXNOCA6xZUV_ZhjZmjPixig0-8Qx8xDV4RTqnM2NiV8KiR9sL156j31PIeGEp5cagk_lbBqxU2XwGJ9hKI5JbkN3Jg_YVltVKpfkdv4hYzlD94hkepWngLi7q5Sn-Zv-LAC4F8WguDTPt6GjTtOCTG3Yl6BuiMDiBy4-xYM_5BhTdJ1ZCF8wAm-o86U1F1bWTGMa7KQksPsb6AGFGpRcAhbxfki6lETARM18DMVaogACUASffA0ppkSAKoRofJ9-EvXWqodItnTtFmW9WurbNjd0otb1BRP_EIJqKDjd4IAgwbNT0h2C4rWUMEji6ET1KgHun7AGAUTF2EnawYpEzYomCR-pM',
id_token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1HTHFqOThWTkxvWGFGZnBKQ0JwZ0I0SmFLcyIsImtpZCI6Ik1HTHFqOThWTkxvWGFGZnBKQ0JwZ0I0SmFLcyJ9.eyJhdWQiOiJlMzg0NTYxMy0wMzMxLTQ5YzAtOWYxMS1mYjZhNjM0MjQyZDIiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9kNWU2ODVhOS0zZTIzLTQyZDktOWVmYS0xNWNjNWMxY2U3ZGIvIiwiaWF0IjoxNzE5NzEwOTM0LCJuYmYiOjE3MTk3MTA5MzQsImV4cCI6MTcxOTcxNDgzNCwiYW1yIjpbInB3ZCIsIm1mYSJdLCJmYW1pbHlfbmFtZSI6Ikd1bXAiLCJnaXZlbl9uYW1lIjoiRm9ycmVzdCIsImlwYWRkciI6IjIyMy4xMDQuNDIuODciLCJuYW1lIjoiRm9ycmVzdCBHdW1wIiwib2lkIjoiOTBmNzU5NmItODhiNi00NzY4LTgyMDQtOGM0NzZhNzNmZTI1IiwicmgiOiIwLkFiY0FxWVhtMVNNLTJVS2UtaFhNWEJ6bjJ4TldoT014QThCSm54SDdhbU5DUXRMOEFQVS4iLCJzdWIiOiJLR2NlTEgtSG9FTklONUg0alZ3d2FMSTJydHBILVMyQ1RuSkNIQTB5MGFrIiwidGlkIjoiZDVlNjg1YTktM2UyMy00MmQ5LTllZmEtMTVjYzVjMWNlN2RiIiwidW5pcXVlX25hbWUiOiJGb3JyZXN0R3VtcEBHaXRjb2lucy5vbm1pY3Jvc29mdC5jb20iLCJ1cG4iOiJGb3JyZXN0R3VtcEBHaXRjb2lucy5vbm1pY3Jvc29mdC5jb20iLCJ1dGkiOiJGSkI0a0xDRnlVV21ud1ZvdE93OEFRIiwidmVyIjoiMS4wIn0.oGP8qczVpNjzG03en1LmF6BcxOHsSsdU5yNDFDzR7nJsNbnIniPFg8Q9Qr6WWknEvrCGdjMWWf9_7lC6HxK6pr0w28txFOwbPsX_sKFXQBgzTzBUhKuVYmaMv9koFf4UhODyncmDtfiEicBKtOEGf3Rqz534hTXmHXPb9wLIz4_SfmZAa_nuV8my7oBYrrY95rRE-ZfsV5DCoSbu3AiIK4_n21EYfKVE_6J6S46X8r5WeQvLOQ1A4CEZI2lIAToWdT2T1fVYxrsMpNpcfcHtGF6HaD1h_0rmy_QOgZ8nClDI2tx-DnTWLjMoqNCzWFY1p1nLPx_60xqACFqVl_3vdQ'
}
Call web API with access_token in authorization headerβ
You will have all right to access resources with access_token. The resources link is like https://login.microsoftonline.com/d5e685a9-3e23-42d9-9efa-15cc5c1ce7db/openid/userinfo. So with access_token in authorization header to call resources link, then RocketServer get the identity info.
async getIdentity(accessToken) {
const params = {};
const headers = {
'User-Agent': this.userAgent, // http://doc.gitlab.com/ce/api/users.html#Current-user
'Accept': 'application/json',
};
if (this.identityTokenSentVia === 'header') {
headers.Authorization = `Bearer ${accessToken}`;
} else {
params[this.accessTokenParam] = accessToken;
}
console.log("XXXXXXXXXXXXXXXXXXXx custom_oauth_server getIdentity path 12", this.identityPath);
console.log("XXXXXXXXXXXXXXXXXXXx custom_oauth_server getIdentity headers 12", headers);
console.log("XXXXXXXXXXXXXXXXXXXx custom_oauth_server getIdentity params 12", params);
try {
const request = await fetch(`${this.identityPath}`, { method: 'GET', headers, params });
console.log("XXXXXXXXXXXXXXXXXXXx custom_oauth_server getIdentity request 12", request);
if (!request.ok) {
throw new Error(request.statusText);
}
const response = await request.json();
console.log("XXXXXXXXXXXXXXXXXXXx custom_oauth_server getIdentity 04", response);
return this.normalizeIdentity(response);
} catch (err) {
const error = new Error(`Failed to fetch identity from ${this.name} at ${this.identityPath}. ${err.message}`);
throw _.extend(error, { response: err.response });
}
}
custom_oauth_server getIdentity headers 12 {
'User-Agent': 'Meteor/METEOR@2.15',
Accept: 'application/json',
Authorization: 'Bearer PAQABAQIAAAApTwJmzXqdR4BN2miheQMYlbd5ZfbyIQUX1iW0FWGmFGvyjTFuy2zHZ1PoDvLgz0D6hAas62MopvXHFIhX53amdiH8_8t-SAGJsjr--GeBeY4Xa3CBx4-1Wg6KutRkg6nZPb-tOO-JSvdlEwlJ_QqPbv_MKZjoqiDbbK0AwbeiQmQm7uTxfk_EjiXxJGM5nYGoHNeg6rslqYYROsjEw2mFf3kyYYFEbUstPfc6Mc_4t3I8QJTNfC0NELnnNHP1b3khDyXtWEeYgcG4v9bNu3_s8MuUJmF1Cv1oWG8p7uTAmpmt84aQ5lnkO3fcxH4yLOH_CPcYLa8JU4K6EaqW7qq8rlvFCTQk0EzWDfhDjfSXESY8zylhFyOqRKsuBVlUiQ7FU42ukPbNDtdJEcwgOm1m9KIFEU5EDUB7ljyPwF_nwgXHnhF6tsU0XEFR3hCAsp0_TgGvtE5xR7mNHC59ZrY86QpcgTHC3lXyDj58VGgZTiAA'
}
Return identity data to RocketChat serverβ
custom_oauth_server getIdentity 04 {
amr: '["pwd","mfa"]',
family_name: 'Gump',
given_name: 'Forrest',
ipaddr: '223.104.42.87',
name: 'Forrest Gump',
oid: '90f7596b-88b6-4768-8204-8c476a73fe25',
rh: '0.AbcAqYXm1SM-2UKe-hXMXBzn2xNWhOMxA8BJnxH7amNCQtL8APU.',
sub: 'KGceLH-HoENIN5H4jVwwaLI2rtpH-S2CTnJCHA0y0ak',
tid: 'd5e685a9-3e23-42d9-9efa-15cc5c1ce7db',
unique_name: 'ForrestGump@Gitcoins.onmicrosoft.com',
upn: 'ForrestGump@Gitcoins.onmicrosoft.com',
uti: 'H1zdM7Pmbk2z9qeOOQUsAQ',
ver: '1.0'
}
Collection Identity Infoβ
At the end of registerService function, all the info of identity will be collected by RocketServer. The RocketServer will find one identity by key field like email or username, then binding the identity credentialToken and credentialSecret for oauth login in next step.
registerService() {
const self = this;
OAuth.registerService(this.name, 2, null, async (query) => {
const response = await self.getAccessToken(query);
const identity = await self.getIdentity(response.access_token, query);
const serviceData = {
_OAuthCustom: true,
serverURL: self.serverURL,
accessToken: response.access_token,
idToken: response.id_token,
expiresAt: +new Date() + 1000 * parseInt(response.expires_in, 10),
};
if (response.refresh_token) {
serviceData.refreshToken = response.refresh_token;
}
_.extend(serviceData, identity);
const data = {
serviceData,
options: {
profile: {
name: identity.name,
},
},
};
return data;
});
}
custom_oauth_server registerService data 05 {
serviceData: {
_OAuthCustom: true,
serverURL: 'https://login.microsoftonline.com/d5e685a9-3e23-42d9-9efa-15cc5c1ce7db',
accessToken: 'PAQABAQIAAAApTwJmzXqdR4BN2miheQMYlbd5ZfbyIQUX1iW0FWGmFGvyjTFuy2zHZ1PoDvLgz0D6hAas62MopvXHFIhX53amdiH8_8t-SAGJsjr--GeBeY4Xa3CBx4-1Wg6KutRkg6nZPb-tOO-JSvdlEwlJ_QqPbv_MKZjoqiDbbK0AwbeiQmQm7uTxfk_EjiXxJGM5nYGoHNeg6rslqYYROsjEw2mFf3kyYYFEbUstPfc6Mc_4t3I8QJTNfC0NELnnNHP1b3khDyXtWEeYgcG4v9bNu3_s8MuUJmF1Cv1oWG8p7uTAmpmt84aQ5lnkO3fcxH4yLOH_CPcYLa8JU4K6EaqW7qq8rlvFCTQk0EzWDfhDjfSXESY8zylhFyOqRKsuBVlUiQ7FU42ukPbNDtdJEcwgOm1m9KIFEU5EDUB7ljyPwF_nwgXHnhF6tsU0XEFR3hCAsp0_TgGvtE5xR7mNHC59ZrY86QpcgTHC3lXyDj58VGgZTiAA',
idToken: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1HTHFqOThWTkxvWGFGZnBKQ0JwZ0I0SmFLcyIsImtpZCI6Ik1HTHFqOThWTkxvWGFGZnBKQ0JwZ0I0SmFLcyJ9.eyJhdWQiOiJlMzg0NTYxMy0wMzMxLTQ5YzAtOWYxMS1mYjZhNjM0MjQyZDIiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9kNWU2ODVhOS0zZTIzLTQyZDktOWVmYS0xNWNjNWMxY2U3ZGIvIiwiaWF0IjoxNzE5NzEwOTM0LCJuYmYiOjE3MTk3MTA5MzQsImV4cCI6MTcxOTcxNDgzNCwiYW1yIjpbInB3ZCIsIm1mYSJdLCJmYW1pbHlfbmFtZSI6Ikd1bXAiLCJnaXZlbl9uYW1lIjoiRm9ycmVzdCIsImlwYWRkciI6IjIyMy4xMDQuNDIuODciLCJuYW1lIjoiRm9ycmVzdCBHdW1wIiwib2lkIjoiOTBmNzU5NmItODhiNi00NzY4LTgyMDQtOGM0NzZhNzNmZTI1IiwicmgiOiIwLkFiY0FxWVhtMVNNLTJVS2UtaFhNWEJ6bjJ4TldoT014QThCSm54SDdhbU5DUXRMOEFQVS4iLCJzdWIiOiJLR2NlTEgtSG9FTklONUg0alZ3d2FMSTJydHBILVMyQ1RuSkNIQTB5MGFrIiwidGlkIjoiZDVlNjg1YTktM2UyMy00MmQ5LTllZmEtMTVjYzVjMWNlN2RiIiwidW5pcXVlX25hbWUiOiJGb3JyZXN0R3VtcEBHaXRjb2lucy5vbm1pY3Jvc29mdC5jb20iLCJ1cG4iOiJGb3JyZXN0R3VtcEBHaXRjb2lucy5vbm1pY3Jvc29mdC5jb20iLCJ1dGkiOiJGSkI0a0xDRnlVV21ud1ZvdE93OEFRIiwidmVyIjoiMS4wIn0.oGP8qczVpNjzG03en1LmF6BcxOHsSsdU5yNDFDzR7nJsNbnIniPFg8Q9Qr6WWknEvrCGdjMWWf9_7lC6HxK6pr0w28txFOwbPsX_sKFXQBgzTzBUhKuVYmaMv9koFf4UhODyncmDtfiEicBKtOEGf3Rqz534hTXmHXPb9wLIz4_SfmZAa_nuV8my7oBYrrY95rRE-ZfsV5DCoSbu3AiIK4_n21EYfKVE_6J6S46X8r5WeQvLOQ1A4CEZI2lIAToWdT2T1fVYxrsMpNpcfcHtGF6HaD1h_0rmy_QOgZ8nClDI2tx-DnTWLjMoqNCzWFY1p1nLPx_60xqACFqVl_3vdQ',
expiresAt: 1719714834151,
refreshToken: '0.AbcAqYXm1SM-2UKe-hXMXBzn2xNWhOMxA8BJnxH7amNCQtL8APU.AgABAwEAAAApTwJmzXqdR4BN2miheQMYAgDs_wUA9P_RBLQedJRbR8Ll3VPCsjY6zuGnnG6AaZjrFXJGr7hxvjrMD8Azvdxc2BPjThB6qx9JVac8azajaKio_azzrov5EgHgPxCoX5tIL4mV-Yg98cVi5Xm8kOrCjUupc6l0qXRec5Eq69VHJwIUyJrsxpPc8LsHYwDNoKhPWm7fBVKUW0g5ifxg_RBAF43MJYrC5z65tgBRmXwsRJ7ZgDBqnXelYWLKgd-3HG5MkuRpv3JB8vxGRSS5kKdEExU0VRXJ08MMVNHIX_bdzFKyf8LYB2V1sU7gxnWnTmDgQAwvS0Asp0XE-nPY9JtUs_NkgsWD_JQEXv40ib3kOXWNf07ZnVD0DC4E-qE2G76CoEq0l0OWjH9EHf_xhLr21skTWRnaRnnQ8UN1dpyNrf5ymSkhxvTXNOCA6xZUV_ZhjZmjPixig0-8Qx8xDV4RTqnM2NiV8KiR9sL156j31PIeGEp5cagk_lbBqxU2XwGJ9hKI5JbkN3Jg_YVltVKpfkdv4hYzlD94hkepWngLi7q5Sn-Zv-LAC4F8WguDTPt6GjTtOCTG3Yl6BuiMDiBy4-xYM_5BhTdJ1ZCF8wAm-o86U1F1bWTGMa7KQksPsb6AGFGpRcAhbxfki6lETARM18DMVaogACUASffA0ppkSAKoRofJ9-EvXWqodItnTtFmW9WurbNjd0otb1BRP_EIJqKDjd4IAgwbNT0h2C4rWUMEji6ET1KgHun7AGAUTF2EnawYpEzYomCR-pM',
amr: '["pwd","mfa"]',
family_name: 'Gump',
given_name: 'Forrest',
ipaddr: '223.104.42.87',
name: 'Gump',
oid: '90f7596b-88b6-4768-8204-8c476a73fe25',
rh: '0.AbcAqYXm1SM-2UKe-hXMXBzn2xNWhOMxA8BJnxH7amNCQtL8APU.',
sub: 'KGceLH-HoENIN5H4jVwwaLI2rtpH-S2CTnJCHA0y0ak',
tid: 'd5e685a9-3e23-42d9-9efa-15cc5c1ce7db',
unique_name: 'ForrestGump@Gitcoins.onmicrosoft.com',
upn: 'ForrestGump@Gitcoins.onmicrosoft.com',
uti: 'H1zdM7Pmbk2z9qeOOQUsAQ',
ver: '1.0',
id: 'KGceLH-HoENIN5H4jVwwaLI2rtpH-S2CTnJCHA0y0ak',
username: 'Forrest',
email: 'ForrestGump@Gitcoins.onmicrosoft.com'
}
Login using authentication providers with credentialToken and credentialSecretβ
After registerService function execution, all the process of authorization finished and the info of identity collected. So which way for one user to login and which authentication provider will be choose? Firstly RocketClient try to login using authentication providers, the following is login call with credentialToken and credentialSecret. The credentialToken and credentialSecret is obtained in the Return credentialToken and credentialSecret step in RocketChatClient.
{
"msg": "method",
"method": "login",
"id":"42",
"params": [
{
"oauth": {
"credentialToken":"credential-token",
"credentialSecret":"credential-secret"
}
}
]
}
Oauth providers loopβ
When Login function executing, updateOrCreateUserFromExternalServiceAsync in custom_oauth_server.js will try to find which Oauth provider to service login. To find Oauth provider with serviceName by looping the provider list in the RocketChatServer, then using addHookToProcessUser funciton to find one dedicated user by key field which have been configured in the RocketChatServer. So the user now have new addtional properties like email2fa and oauth type for login.
const { updateOrCreateUserFromExternalService } = Accounts;
const updateOrCreateUserFromExternalServiceAsync = async function (...args /* serviceName, serviceData, options*/) {
for await (const hook of BeforeUpdateOrCreateUserFromExternalService) {
console.log("XXXXXXXXXXXXXXXXXXXx custom_oauth_server hook hook hook hook hook XXXXXXXXXXXXXXXXXXXXXXXXXXx");
await hook.apply(this, args);
}
const [serviceName, serviceData] = args;
const user = updateOrCreateUserFromExternalService.apply(this, args);
const fullUser = await Users.findOneById(user.userId);
if (settings.get('LDAP_Update_Data_On_OAuth_Login')) {
await LDAP.loginAuthenticatedUserRequest(fullUser.username);
}
await callbacks.run('afterValidateNewOAuthUser', {
identity: serviceData,
serviceName,
user: fullUser,
});
return user;
};
addHookToProcessUser() {
BeforeUpdateOrCreateUserFromExternalService.push(async (serviceName, serviceData /* , options*/) => {
console.log("XXXXXXXXXXXXXXXXXXXx custom_oauth_server addHookToProcessUser serviceName 12", serviceName);
console.log("XXXXXXXXXXXXXXXXXXXx custom_oauth_server addHookToProcessUser this.name 12", this.name);
console.log("XXXXXXXXXXXXXXXXXXXx custom_oauth_server addHookToProcessUser this.keyField 12", this.keyField);
console.log("XXXXXXXXXXXXXXXXXXXx custom_oauth_server addHookToProcessUser serviceData 12", serviceData);
if (serviceName !== this.name) {
return;
}
if (serviceData.username) {
let user = undefined;
if (this.keyField === 'username') {
user = this.mergeUsersDistinctServices
? await Users.findOneByUsernameIgnoringCase(serviceData.username)
: await Users.findOneByUsernameAndServiceNameIgnoringCase(serviceData.username, serviceData.id, serviceName);
} else if (this.keyField === 'email') {
user = this.mergeUsersDistinctServices
? await Users.findOneByEmailAddress(serviceData.email)
: await Users.findOneByEmailAddressAndServiceNameIgnoringCase(serviceData.email, serviceData.id, serviceName);
}
if (!user) {
console.log("XXXXXXXXXXXXXXXXXXXXXXXXXXXXx gitcoins return 1");
return;
}
await callbacks.run('afterProcessOAuthUser', { serviceName, serviceData, user });
console.log("XXXXXXXXXXXXXXXXXXXXXXXXXXXXx gitcoins user 18", user);
// User already created or merged and has identical name as before
if (
user.services &&
user.services[serviceName] &&
user.services[serviceName].id === serviceData.id &&
user.name === serviceData.name &&
(this.keyField === 'email' || !serviceData.email || user.emails?.find(({ address }) => address === serviceData.email))
) {
console.log("XXXXXXXXXXXXXXXXXXXXXXXXXXXXx gitcoins return 2");
return;
}
console.log("XXXXXXXXXXXXXXXXXXXXXXXXXXXXx gitcoins user 19", user);
if (this.mergeUsers !== true) {
throw new Meteor.Error('CustomOAuth', `User with username ${user.username} already exists`);
}
const serviceIdKey = `services.${serviceName}.id`;
const update = {
$set: {
name: serviceData.name,
...(this.keyField === 'username' && serviceData.email && { emails: [{ address: serviceData.email, verified: true }] }),
[serviceIdKey]: serviceData.id,
},
};
console.log("XXXXXXXXXXXXXXXXXXXXXXXXXXXXx gitcoins user 20", user);
await Users.update({ _id: user._id }, update);
}
});
XXXXXXXXXXXXXXXXXXXx custom_oauth_server hook hook hook hook hook XXXXXXXXXXXXXXXXXXXXXXXXXXx
XXXXXXXXXXXXXXXXXXXx custom_oauth_server addHookToProcessUser serviceName 12 365oauth
XXXXXXXXXXXXXXXXXXXx custom_oauth_server addHookToProcessUser this.name 12 gitlab
XXXXXXXXXXXXXXXXXXXx custom_oauth_server addHookToProcessUser this.keyField 12 undefined
XXXXXXXXXXXXXXXXXXXx custom_oauth_server addHookToProcessUser serviceData 12 {
_OAuthCustom: true,
serverURL: 'https://login.microsoftonline.com/d5e685a9-3e23-42d9-9efa-15cc5c1ce7db',
accessToken: 'PAQABAQIAAAApTwJmzXqdR4BN2miheQMYlbd5ZfbyIQUX1iW0FWGmFGvyjTFuy2zHZ1PoDvLgz0D6hAas62MopvXHFIhX53amdiH8_8t-SAGJsjr--GeBeY4Xa3CBx4-1Wg6KutRkg6nZPb-tOO-JSvdlEwlJ_QqPbv_MKZjoqiDbbK0AwbeiQmQm7uTxfk_EjiXxJGM5nYGoHNeg6rslqYYROsjEw2mFf3kyYYFEbUstPfc6Mc_4t3I8QJTNfC0NELnnNHP1b3khDyXtWEeYgcG4v9bNu3_s8MuUJmF1Cv1oWG8p7uTAmpmt84aQ5lnkO3fcxH4yLOH_CPcYLa8JU4K6EaqW7qq8rlvFCTQk0EzWDfhDjfSXESY8zylhFyOqRKsuBVlUiQ7FU42ukPbNDtdJEcwgOm1m9KIFEU5EDUB7ljyPwF_nwgXHnhF6tsU0XEFR3hCAsp0_TgGvtE5xR7mNHC59ZrY86QpcgTHC3lXyDj58VGgZTiAA',
idToken: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1HTHFqOThWTkxvWGFGZnBKQ0JwZ0I0SmFLcyIsImtpZCI6Ik1HTHFqOThWTkxvWGFGZnBKQ0JwZ0I0SmFLcyJ9.eyJhdWQiOiJlMzg0NTYxMy0wMzMxLTQ5YzAtOWYxMS1mYjZhNjM0MjQyZDIiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9kNWU2ODVhOS0zZTIzLTQyZDktOWVmYS0xNWNjNWMxY2U3ZGIvIiwiaWF0IjoxNzE5NzEwOTM0LCJuYmYiOjE3MTk3MTA5MzQsImV4cCI6MTcxOTcxNDgzNCwiYW1yIjpbInB3ZCIsIm1mYSJdLCJmYW1pbHlfbmFtZSI6Ikd1bXAiLCJnaXZlbl9uYW1lIjoiRm9ycmVzdCIsImlwYWRkciI6IjIyMy4xMDQuNDIuODciLCJuYW1lIjoiRm9ycmVzdCBHdW1wIiwib2lkIjoiOTBmNzU5NmItODhiNi00NzY4LTgyMDQtOGM0NzZhNzNmZTI1IiwicmgiOiIwLkFiY0FxWVhtMVNNLTJVS2UtaFhNWEJ6bjJ4TldoT014QThCSm54SDdhbU5DUXRMOEFQVS4iLCJzdWIiOiJLR2NlTEgtSG9FTklONUg0alZ3d2FMSTJydHBILVMyQ1RuSkNIQTB5MGFrIiwidGlkIjoiZDVlNjg1YTktM2UyMy00MmQ5LTllZmEtMTVjYzVjMWNlN2RiIiwidW5pcXVlX25hbWUiOiJGb3JyZXN0R3VtcEBHaXRjb2lucy5vbm1pY3Jvc29mdC5jb20iLCJ1cG4iOiJGb3JyZXN0R3VtcEBHaXRjb2lucy5vbm1pY3Jvc29mdC5jb20iLCJ1dGkiOiJGSkI0a0xDRnlVV21ud1ZvdE93OEFRIiwidmVyIjoiMS4wIn0.oGP8qczVpNjzG03en1LmF6BcxOHsSsdU5yNDFDzR7nJsNbnIniPFg8Q9Qr6WWknEvrCGdjMWWf9_7lC6HxK6pr0w28txFOwbPsX_sKFXQBgzTzBUhKuVYmaMv9koFf4UhODyncmDtfiEicBKtOEGf3Rqz534hTXmHXPb9wLIz4_SfmZAa_nuV8my7oBYrrY95rRE-ZfsV5DCoSbu3AiIK4_n21EYfKVE_6J6S46X8r5WeQvLOQ1A4CEZI2lIAToWdT2T1fVYxrsMpNpcfcHtGF6HaD1h_0rmy_QOgZ8nClDI2tx-DnTWLjMoqNCzWFY1p1nLPx_60xqACFqVl_3vdQ',
expiresAt: 1719714834151,
refreshToken: '0.AbcAqYXm1SM-2UKe-hXMXBzn2xNWhOMxA8BJnxH7amNCQtL8APU.AgABAwEAAAApTwJmzXqdR4BN2miheQMYAgDs_wUA9P_RBLQedJRbR8Ll3VPCsjY6zuGnnG6AaZjrFXJGr7hxvjrMD8Azvdxc2BPjThB6qx9JVac8azajaKio_azzrov5EgHgPxCoX5tIL4mV-Yg98cVi5Xm8kOrCjUupc6l0qXRec5Eq69VHJwIUyJrsxpPc8LsHYwDNoKhPWm7fBVKUW0g5ifxg_RBAF43MJYrC5z65tgBRmXwsRJ7ZgDBqnXelYWLKgd-3HG5MkuRpv3JB8vxGRSS5kKdEExU0VRXJ08MMVNHIX_bdzFKyf8LYB2V1sU7gxnWnTmDgQAwvS0Asp0XE-nPY9JtUs_NkgsWD_JQEXv40ib3kOXWNf07ZnVD0DC4E-qE2G76CoEq0l0OWjH9EHf_xhLr21skTWRnaRnnQ8UN1dpyNrf5ymSkhxvTXNOCA6xZUV_ZhjZmjPixig0-8Qx8xDV4RTqnM2NiV8KiR9sL156j31PIeGEp5cagk_lbBqxU2XwGJ9hKI5JbkN3Jg_YVltVKpfkdv4hYzlD94hkepWngLi7q5Sn-Zv-LAC4F8WguDTPt6GjTtOCTG3Yl6BuiMDiBy4-xYM_5BhTdJ1ZCF8wAm-o86U1F1bWTGMa7KQksPsb6AGFGpRcAhbxfki6lETARM18DMVaogACUASffA0ppkSAKoRofJ9-EvXWqodItnTtFmW9WurbNjd0otb1BRP_EIJqKDjd4IAgwbNT0h2C4rWUMEji6ET1KgHun7AGAUTF2EnawYpEzYomCR-pM',
amr: '["pwd","mfa"]',
family_name: 'Gump',
given_name: 'Forrest',
ipaddr: '223.104.42.87',
name: 'Gump',
oid: '90f7596b-88b6-4768-8204-8c476a73fe25',
rh: '0.AbcAqYXm1SM-2UKe-hXMXBzn2xNWhOMxA8BJnxH7amNCQtL8APU.',
sub: 'KGceLH-HoENIN5H4jVwwaLI2rtpH-S2CTnJCHA0y0ak',
tid: 'd5e685a9-3e23-42d9-9efa-15cc5c1ce7db',
unique_name: 'ForrestGump@Gitcoins.onmicrosoft.com',
upn: 'ForrestGump@Gitcoins.onmicrosoft.com',
uti: 'H1zdM7Pmbk2z9qeOOQUsAQ',
ver: '1.0',
id: 'KGceLH-HoENIN5H4jVwwaLI2rtpH-S2CTnJCHA0y0ak',
username: 'Forrest',
email: 'ForrestGump@Gitcoins.onmicrosoft.com'
}
XXXXXXXXXXXXXXXXXXXx custom_oauth_server hook hook hook hook hook XXXXXXXXXXXXXXXXXXXXXXXXXXx
XXXXXXXXXXXXXXXXXXXx custom_oauth_server addHookToProcessUser serviceName 12 365oauth
XXXXXXXXXXXXXXXXXXXx custom_oauth_server addHookToProcessUser this.name 12 365oauth
XXXXXXXXXXXXXXXXXXXx custom_oauth_server addHookToProcessUser this.keyField 12 username
XXXXXXXXXXXXXXXXXXXx custom_oauth_server addHookToProcessUser serviceData 12 {
_OAuthCustom: true,
serverURL: 'https://login.microsoftonline.com/d5e685a9-3e23-42d9-9efa-15cc5c1ce7db',
accessToken: 'PAQABAQIAAAApTwJmzXqdR4BN2miheQMYlbd5ZfbyIQUX1iW0FWGmFGvyjTFuy2zHZ1PoDvLgz0D6hAas62MopvXHFIhX53amdiH8_8t-SAGJsjr--GeBeY4Xa3CBx4-1Wg6KutRkg6nZPb-tOO-JSvdlEwlJ_QqPbv_MKZjoqiDbbK0AwbeiQmQm7uTxfk_EjiXxJGM5nYGoHNeg6rslqYYROsjEw2mFf3kyYYFEbUstPfc6Mc_4t3I8QJTNfC0NELnnNHP1b3khDyXtWEeYgcG4v9bNu3_s8MuUJmF1Cv1oWG8p7uTAmpmt84aQ5lnkO3fcxH4yLOH_CPcYLa8JU4K6EaqW7qq8rlvFCTQk0EzWDfhDjfSXESY8zylhFyOqRKsuBVlUiQ7FU42ukPbNDtdJEcwgOm1m9KIFEU5EDUB7ljyPwF_nwgXHnhF6tsU0XEFR3hCAsp0_TgGvtE5xR7mNHC59ZrY86QpcgTHC3lXyDj58VGgZTiAA',
idToken: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1HTHFqOThWTkxvWGFGZnBKQ0JwZ0I0SmFLcyIsImtpZCI6Ik1HTHFqOThWTkxvWGFGZnBKQ0JwZ0I0SmFLcyJ9.eyJhdWQiOiJlMzg0NTYxMy0wMzMxLTQ5YzAtOWYxMS1mYjZhNjM0MjQyZDIiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9kNWU2ODVhOS0zZTIzLTQyZDktOWVmYS0xNWNjNWMxY2U3ZGIvIiwiaWF0IjoxNzE5NzEwOTM0LCJuYmYiOjE3MTk3MTA5MzQsImV4cCI6MTcxOTcxNDgzNCwiYW1yIjpbInB3ZCIsIm1mYSJdLCJmYW1pbHlfbmFtZSI6Ikd1bXAiLCJnaXZlbl9uYW1lIjoiRm9ycmVzdCIsImlwYWRkciI6IjIyMy4xMDQuNDIuODciLCJuYW1lIjoiRm9ycmVzdCBHdW1wIiwib2lkIjoiOTBmNzU5NmItODhiNi00NzY4LTgyMDQtOGM0NzZhNzNmZTI1IiwicmgiOiIwLkFiY0FxWVhtMVNNLTJVS2UtaFhNWEJ6bjJ4TldoT014QThCSm54SDdhbU5DUXRMOEFQVS4iLCJzdWIiOiJLR2NlTEgtSG9FTklONUg0alZ3d2FMSTJydHBILVMyQ1RuSkNIQTB5MGFrIiwidGlkIjoiZDVlNjg1YTktM2UyMy00MmQ5LTllZmEtMTVjYzVjMWNlN2RiIiwidW5pcXVlX25hbWUiOiJGb3JyZXN0R3VtcEBHaXRjb2lucy5vbm1pY3Jvc29mdC5jb20iLCJ1cG4iOiJGb3JyZXN0R3VtcEBHaXRjb2lucy5vbm1pY3Jvc29mdC5jb20iLCJ1dGkiOiJGSkI0a0xDRnlVV21ud1ZvdE93OEFRIiwidmVyIjoiMS4wIn0.oGP8qczVpNjzG03en1LmF6BcxOHsSsdU5yNDFDzR7nJsNbnIniPFg8Q9Qr6WWknEvrCGdjMWWf9_7lC6HxK6pr0w28txFOwbPsX_sKFXQBgzTzBUhKuVYmaMv9koFf4UhODyncmDtfiEicBKtOEGf3Rqz534hTXmHXPb9wLIz4_SfmZAa_nuV8my7oBYrrY95rRE-ZfsV5DCoSbu3AiIK4_n21EYfKVE_6J6S46X8r5WeQvLOQ1A4CEZI2lIAToWdT2T1fVYxrsMpNpcfcHtGF6HaD1h_0rmy_QOgZ8nClDI2tx-DnTWLjMoqNCzWFY1p1nLPx_60xqACFqVl_3vdQ',
expiresAt: 1719714834151,
refreshToken: '0.AbcAqYXm1SM-2UKe-hXMXBzn2xNWhOMxA8BJnxH7amNCQtL8APU.AgABAwEAAAApTwJmzXqdR4BN2miheQMYAgDs_wUA9P_RBLQedJRbR8Ll3VPCsjY6zuGnnG6AaZjrFXJGr7hxvjrMD8Azvdxc2BPjThB6qx9JVac8azajaKio_azzrov5EgHgPxCoX5tIL4mV-Yg98cVi5Xm8kOrCjUupc6l0qXRec5Eq69VHJwIUyJrsxpPc8LsHYwDNoKhPWm7fBVKUW0g5ifxg_RBAF43MJYrC5z65tgBRmXwsRJ7ZgDBqnXelYWLKgd-3HG5MkuRpv3JB8vxGRSS5kKdEExU0VRXJ08MMVNHIX_bdzFKyf8LYB2V1sU7gxnWnTmDgQAwvS0Asp0XE-nPY9JtUs_NkgsWD_JQEXv40ib3kOXWNf07ZnVD0DC4E-qE2G76CoEq0l0OWjH9EHf_xhLr21skTWRnaRnnQ8UN1dpyNrf5ymSkhxvTXNOCA6xZUV_ZhjZmjPixig0-8Qx8xDV4RTqnM2NiV8KiR9sL156j31PIeGEp5cagk_lbBqxU2XwGJ9hKI5JbkN3Jg_YVltVKpfkdv4hYzlD94hkepWngLi7q5Sn-Zv-LAC4F8WguDTPt6GjTtOCTG3Yl6BuiMDiBy4-xYM_5BhTdJ1ZCF8wAm-o86U1F1bWTGMa7KQksPsb6AGFGpRcAhbxfki6lETARM18DMVaogACUASffA0ppkSAKoRofJ9-EvXWqodItnTtFmW9WurbNjd0otb1BRP_EIJqKDjd4IAgwbNT0h2C4rWUMEji6ET1KgHun7AGAUTF2EnawYpEzYomCR-pM',
amr: '["pwd","mfa"]',
family_name: 'Gump',
given_name: 'Forrest',
ipaddr: '223.104.42.87',
name: 'Gump',
oid: '90f7596b-88b6-4768-8204-8c476a73fe25',
rh: '0.AbcAqYXm1SM-2UKe-hXMXBzn2xNWhOMxA8BJnxH7amNCQtL8APU.',
sub: 'KGceLH-HoENIN5H4jVwwaLI2rtpH-S2CTnJCHA0y0ak',
tid: 'd5e685a9-3e23-42d9-9efa-15cc5c1ce7db',
unique_name: 'ForrestGump@Gitcoins.onmicrosoft.com',
upn: 'ForrestGump@Gitcoins.onmicrosoft.com',
uti: 'H1zdM7Pmbk2z9qeOOQUsAQ',
ver: '1.0',
id: 'KGceLH-HoENIN5H4jVwwaLI2rtpH-S2CTnJCHA0y0ak',
username: 'Forrest',
email: 'ForrestGump@Gitcoins.onmicrosoft.com'
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXx gitcoins return 2
..................... authenticaiton server Account validateLoginAttemptAsync 10 {
type: '365oauth',
allowed: true,
methodName: 'login',
methodArguments: [ { oauth: [Object] } ],
user: {
_id: 'HMDuxBovxYtNuxtuE',
createdAt: 2024-06-05T04:06:25.944Z,
services: {
email2fa: [Object],
email: [Object],
resume: [Object],
'365oauth': [Object],
password: [Object],
microsoft356: [Object],
emailCode: [Object]
},
emails: [ [Object] ],
type: 'user',
roles: [ 'user' ],
status: 'offline',
active: true,
_updatedAt: 2024-06-25T01:51:50.840Z,
name: 'Gump',
username: 'forrest',
__rooms: [ 'GENERAL' ],
lastLogin: 2024-06-25T01:51:13.025Z,
statusConnection: 'offline',
utcOffset: 8,
statusText: '',
statusDefault: 'online',
settings: { profile: {} }
},
connection: {
id: 'hDZQhgrS925j22fZN',
close: [Function: close],
httpHeaders: {
'x-forwarded-port': '80',
'x-forwarded-proto': 'https,http',
'x-forwarded-host': '212c-34-92-204-228.ngrok-free.app',
'x-forwarded-for': '2a11:3:200::2057,127.0.0.1',
'sec-fetch-site': 'same-origin',
'sec-fetch-mode': 'cors',
'sec-fetch-dest': 'empty',
'sec-ch-ua-platform': '"Windows"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"',
referer: 'https://212c-34-92-204-228.ngrok-free.app/home',
priority: 'u=1, i',
origin: 'https://212c-34-92-204-228.ngrok-free.app',
'content-type': 'application/json',
'accept-language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7',
'accept-encoding': 'gzip, deflate, br, zstd',
accept: 'application/json',
'content-length': '232',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',
host: '212c-34-92-204-228.ngrok-free.app',
connection: 'keep-alive'
},
clientAddress: '127.0.0.1',
token: undefined
}
}
2FA loginβ
2FA (Two-Factor Authentication) in the RocketChat context, after verify the identiy info from Identity Authorization Platform, the TOTP (Time-Based One-Time Password) will be send out to your email. After input TOTP to login page, then login successfuly. When try to 2FA with TOTP, there is exeception happend to trigger the RocketChatServer to send the TOTP to your email.
Exception while invoking method login errorClass [Error]: TOTP Required [totp-required]
at app/2fa/server/code/index.ts:211:9
at /home/gitcoins/.meteor/packages/promise/.0.12.2.1v51k7w.6dob++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
=> awaited here:
at Function.Promise.await (/home/gitcoins/.meteor/packages/promise/.0.12.2.1v51k7w.6dob++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/promise_server.js:56:12)
at app/2fa/server/loginHandler.ts:44:3
at /home/gitcoins/.meteor/packages/promise/.0.12.2.1v51k7w.6dob++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
=> awaited here:
at Function.Promise.await (/home/gitcoins/.meteor/packages/promise/.0.12.2.1v51k7w.6dob++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/promise_server.js:56:12)
at lib/callbacks/callbacksBase.ts:68:5
at /home/gitcoins/.meteor/packages/promise/.0.12.2.1v51k7w.6dob++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
=> awaited here:
at Function.Promise.await (/home/gitcoins/.meteor/packages/promise/.0.12.2.1v51k7w.6dob++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/promise_server.js:56:12)
at app/authentication/server/startup/index.js:434:7
at /home/gitcoins/.meteor/packages/promise/.0.12.2.1v51k7w.6dob++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
=> awaited here:
at Function.Promise.await (/home/gitcoins/.meteor/packages/promise/.0.12.2.1v51k7w.6dob++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/promise_server.js:56:12)
at app/authentication/server/startup/index.js:455:17
at packages/callback-hook/hook.js:177:18
at packages/meteor.js:365:18
at packages/accounts-base/accounts_server.js:226:15
at Hook.forEach (packages/callback-hook/hook.js:124:15)
at AccountsServer._validateLogin (packages/accounts-base/accounts_server.js:223:29)
at packages/accounts-base/accounts_server.js:473:10
at /home/gitcoins/.meteor/packages/promise/.0.12.2.1v51k7w.6dob++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
=> awaited here:
at Function.Promise.await (/home/gitcoins/.meteor/packages/promise/.0.12.2.1v51k7w.6dob++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/promise_server.js:56:12)
at packages/accounts-base/accounts_server.js:653:7
at /home/gitcoins/.meteor/packages/promise/.0.12.2.1v51k7w.6dob++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
=> awaited here:
at Function.Promise.await (/home/gitcoins/.meteor/packages/promise/.0.12.2.1v51k7w.6dob++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/promise_server.js:56:12)
at packages/meteor.js:367:22
at Meteor.EnvironmentVariable.EVp.withValue (packages/meteor.js:1389:31)
at packages/ddp-server/livedata_server.js:1854:36
at new Promise (<anonymous>)
at Server.applyAsync (packages/ddp-server/livedata_server.js:1853:12)
at Server.callAsync (packages/ddp-server/livedata_server.js:1779:17)
at app/api/server/v1/misc.ts:620:33
at /home/gitcoins/.meteor/packages/promise/.0.12.2.1v51k7w.6dob++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
=> awaited here:
at Function.Promise.await (/home/gitcoins/.meteor/packages/promise/.0.12.2.1v51k7w.6dob++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/promise_server.js:56:12)
at app/api/server/v1/misc.ts:620:17
at /home/gitcoins/.meteor/packages/promise/.0.12.2.1v51k7w.6dob++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40 {
isClientSafe: true,
error: 'totp-required',
reason: 'TOTP Required',
details: {
method: 'email',
codeGenerated: true,
emailOrUsername: 'forrest',
availableMethods: [ 'email' ]
},
errorType: 'Meteor.Error'
}
====== BEGIN MAIL #34 ======
(Mail not sent; to enable sending, set the MAIL_URL environment variable.)
Content-Type: multipart/alternative;
boundary="--_NmP-7245379ef3eb6267-Part_1"
To: ForrestGump@gitcoins.onmicrosoft.com
Subject: Authentication code
Message-ID: <b062f356-f598-1d0e-d7e4-5728ab80ebaa@rocket-chat-server>
Date: Sun, 30 Jun 2024 01:34:00 +0000
MIME-Version: 1.0
----_NmP-7245379ef3eb6267-Part_1
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 7bit
Here is your authentication code:
274-035
- credentialToken in all steps must be same in one login session.
- redirect_uri in all steps must be same in one login session.
- state in all steps must be same in one login session.