Rocket Chat - Login via Rest API - rocket.chat

I'm attempting to login to my Rocket Chat install using the REST API. The login call comes back successful and I receive the AuthToken and userID. But I can not seem to bypass the login screen. In other words what I want to do is use my app to send over the login request and then because it is successful bypass the Rocket Chat login screen and go straight into the chat.
Thanks!

Logging in through Rocket.Chat's REST API and expecting the web browser to not require you to login again requires a few more steps.
When you authenticate with the REST API successfully the resulting object looks like (these are from a local development server):
{
"status": "success",
"data": {
"authToken": "t2hjaCXP397DxwnYAgQtEaAULDjo4S5vXkopLr04LZL",
"userId": "Q4KzBWSGtcCiKTtvC"
}
}
From this result, you will need to take the authToken and set your in your web browser's LocalStorage Meteor.loginToken with the value of authToken. For example in this case, we would do the following:
var authToken = 'cXvkTF8X4uu-J_2uWelJGt4iuuTxjD5pFHuqhLOQRLJ';
localStorage.setItem('Meteor.loginToken', authToken);
Shortly after doing that, your Rocket.Chat screen should refresh and now you are logged in.

You can insert the token as resumeToken (previously obtained as authToken) in the same HTML:
https://yourown.rocket.chat/home?resumeToken=abcd123456789
(from https://docs.rocket.chat/guides/administrator-guides/authentication)

Related

How to refresh id-token using #microsoft/teamsfx

I created a Teams tab application by customizing the SSO react app sample from the Teams toolkit. The application redirects the user to our website (inside one of the tabs). I can grab the id-token in react (teamsfx.getCredentials().getToken("")) and pass it to our web application via a query parameter.
This id-token is validated and then passed around to various microservices that comprise our backend.
This part works well, but then, we had the need to refresh the token. So, we decided for the web application (written in Angular) to fetch the token using #microsoft/teamsfx and #microsoft/teams-js npm packages.
While I am not certain if that is the way to go, when I execute the following code inside an angular service, it throws the "SDK initialization timed out" error.
try {
const teamsFx: TeamsFx = new TeamsFx(IdentityType.User, {
"clientId": "ee89fb47-a378-4096-b893-**********",
"objectId": "df568fe9-3d33-4b22-94fc-**********",
"oauth2PermissionScopeId": "4ce5bb24-585a-40d3-9891-************",
"tenantId": "5d65ee67-1073-4979-884c-**************",
"oauthHost": "https://login.microsoftonline.com",
"oauthAuthority": "https://login.microsoftonline.com/5d65ee67-1073-4979-884c-****************",
"applicationIdUris": "api://localhost/ee89fb47-a378-4096-b893-***************",
"frontendEndpoint": "https://localhost",
"initiateLoginEndpoint": "https://localhost:8101"
});
const creds = await teamsFx.getCredential().getToken('https://graph.microsoft.com/User.Read');
const token = creds?.token;
console.log("New Token: ", token);
const expirationTimestamp = creds?.expiresOnTimestamp;
this.scheduleRefresh(expirationTimestamp);
this.tokenRefreshed.next({ token: token, expiresOnTimestamp: expirationTimestamp });
}
catch (error) {
console.error("Error in getNewTeamsToken(): ", error);
}
Am I missing anything here, or is the approach itself wrong? Please advise.
Teams is basically just using MSAL under the covers (with a bit of other stuff thrown in I think) to get the token. If you want to be able to authenticate your user outside of Teams, in a separate web app, you can simply use MSAL yourself (something like this I'd guess: https://learn.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-angular-auth-code).
That would mean, essentially, that you have a tab web app, doing Teams SSO and then a separate standalone Angular app doing direct MSAL auth. Does you tab app actually do anything? If not, and you're only using it to redirect, you can instead combine these into a single app, and detect in the app whether you're in Teams or not - if you are, do SSO using TeamsFX. If not, do MSAL login directly. This link shows how to detect if your inside of Teams or not.
If you want to continue with separate apps that's fine, but I absolutely would not pass the token as a QueryString parameter - that is extremely dangerous. First of all, tokens are not meant to be passed like this at all, as they could be intercepted. Secondly, passing them on Querystring means that they are entirely open - anything inbetween can sniff the address and lift out your token (if it was a POST payload with httpS, for instance, at least the 'S' would be encrypting the token between browser and server).

Google API: use offline access token in javascript

I started a project using the Google API signin mixed with an angularJS+Firebase app.
What I would like to do is to be able to send an e-mail from one person to another programmatically.
Example: John is logged in, clicks on a button which sends an email to Rachel. But that email is sent using the stored token from Ted, not John's account.
It seems possible using the php library which is not an option here.
So far, I get the token easily using these few lines:
var GoogleAuth = gapi.auth2.getAuthInstance();
GoogleAuth.grantOfflineAccess({
scope: 'https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/drive https://mail.google.com/ profile email'
}).then(function(resp) {
console.log(resp);
this.storeToken(resp.code);
});
Is it actually possible ?
A quick search just got me results for php or about how you get a token with the JS library... not how to use it !
From my understanding you want to use a refresh token ( offline access ) to send an email from Ted's account via Javascript.
Sadly this is not possible client side. What your code gives you is a 'code' that you can send to your server using a $http.post () and trade with Google server side for a refresh token.
Here is a guide for how to change that code into a refresh token.
While you can do this client side it would involve exposing your client secret which you should never do.(https://developers.google.com/identity/sign-in/web/server-side-flow)
Every time John wants to send an email from Ted's account your application will have to send a request to your server that:
Sends a request to google with the refresh token and generates an access token (https://developers.google.com/identity/protocols/OAuth2WebServer#offline)
Sends a seccond request to google using the access token to send the email from Ted's account
I hope that this helped.

How to exchange Google one-time authorization code for a refresh token without callback (intranet)?

I'm working on a intranet-based application and I want to use Google services. Currently I have successfully implemented Google Authentication with "Sign-In for Websites" using JavaScript client-side authentication. My users can now sign in or sign up with their Google accounts.
Now I want to use Google API to create and share Google Sheets with my users. These documents will be created with a specific Google account and then shared with my users.
This is why I want to use this server-slide flow to get a one-time authorization code and exchange it for a refresh token:
https://developers.google.com/identity/sign-in/web/server-side-flow
This refresh token will be stored in my database allowing me to user Google services on behalf of this offline user.
Using JavaScript library, I was able to get the one-time authorization code that I send to my server with a AJAX request.
auth2.grantOfflineAccess({'redirect_uri': 'postmessage'}).then(grantOfflineAccessCallback);
var grantOfflineAccessCallback = function(authResult) {
var auth_code = authResult.code;
// Exchange the one-time authorization code for tokens
$.post(...);
}
On server-side I use Google API PHP Client (v2.0.0-RC6) to acquire an access and refresh token.
$this->client = new Google_Client();
$this->client->setClientId($this->clientId);
$this->client->setClientSecret($this->clientSecret);
$this->client->setAccessType('offline');
$this->client->setApprovalPrompt('force');
$response = $this->client->fetchAccessTokenWithAuthCode($oneTimeCode);
I wasn't able to exchange the authorization code.
Client error: `POST https://www.googleapis.com/oauth2/v4/token` resulted in a `400 Bad Request` response:
{
"error": "invalid_request",
"error_description": "Missing parameter: redirect_uri"
}
On this page we can read:
On the server, exchange the auth code for access and refresh tokens.
Use the access token to call Google APIs on behalf of the user.
On the JAVA example code:
REDIRECT_URI: // Specify the same redirect URI that you use with your web
// app. If you don't have a web version of your app, you can
// specify an empty string.
Because the application I working on is an intranet application, I tried to specify an empty string for this redirect_uri parameter before calling fetchAccessTokenWithAuthCode() method:
$this->client->setRedirectUri('');
... result in Redirect URI must be absolute.
Can we use this hybrid server-slide flow without callback URL?
Is there any solution to my problem?
Thanks,
Edit:
redirect_uri is where the user will be redirected to after he signed in. This URL must be registered in the Google Project (developers console). So redirect_uri is NOT the callback...!
Problem is now solved with:
$this->client->setRedirectUri('http://same.url.as.in.developers.console/');

How do I authorise an app (web or installed) without user intervention?

Let's say that I have a web app ("mydriveapp") that needs to access Drive files in a background service. It will either own the files it is accessing, or be run in a Google Account with which the owner has shared the documents.
I understand that my app needs a refresh token, but I don't want to write the code to obtain that since I'll only ever do it once.
NB. This is NOT using a Service Account. The app will be run under a conventional Google account. Service Account is a valid approach in some situations. However the technique of using Oauth Playground to simulate the app can save a bunch of redundant effort, and applies to any APIs for which sharing to a Service Account is unsupported.
NB June 2022. It seems that Google have updated their verification requirements which adds additional steps (or negates the approach - depending on your point of view).
See recent comments for more detail
This can be done with the Oauth2 Playground at https://developers.google.com/oauthplayground
Steps:-
Create the Google Account (eg. my.drive.app#gmail.com) - Or skip this step if you are using an existing account.
Use the API console to register the mydriveapp (https://console.developers.google.com/apis/credentials/oauthclient?project=mydriveapp or just https://console.developers.google.com/apis/)
Create a new set of credentials. Credentials/Create Credentials/OAuth Client Id then select Web application
Include https://developers.google.com/oauthplayground as a valid redirect URI
Note the client ID (web app) and Client Secret
Login as my.drive.app#gmail.com
Go to Oauth2 playground
In Settings (gear icon), set
OAuth flow: Server-side
Access type: Offline
Use your own OAuth credentials: TICK
Client Id and Client Secret: from step 5
Click Step 1 and choose Drive API v3 https://www.googleapis.com/auth/drive (having said that, this technique also works for any of the Google APIs listed)
Click Authorize APIs. You will be prompted to choose your Google account and confirm access
Click Step 2 and "Exchange authorization code for tokens"
Copy the returned Refresh token and paste it into your app, source code or in to some form of storage from where your app can retrieve it.
Your app can now run unattended, and use the Refresh Token as described https://developers.google.com/accounts/docs/OAuth2WebServer#offline to obtain an Access Token.
NB. Be aware that the refresh token can be expired by Google which will mean that you need to repeat steps 5 onwards to get a new refresh token. The symptom of this will be a Invalid Grant returned when you try to use the refresh token.
NB2. This technique works well if you want a web app which access your own (and only your own) Drive account, without bothering to write the authorization code which would only ever be run once. Just skip step 1, and replace "my.drive.app" with your own email address in step 6. make sure you are aware of the security implications if the Refresh Token gets stolen.
See Woody's comment below where he links to this Google video https://www.youtube.com/watch?v=hfWe1gPCnzc
.
.
.
Here is a quick JavaScript routine that shows how to use the Refresh Token from the OAuth Playground to list some Drive files. You can simply copy-paste it into Chrome dev console, or run it with node. Of course provide your own credentials (the ones below are all fake).
function get_access_token_using_saved_refresh_token() {
// from the oauth playground
const refresh_token = "1/0PvMAoF9GaJFqbNsLZQg-f9NXEljQclmRP4Gwfdo_0";
// from the API console
const client_id = "559798723558-amtjh114mvtpiqis80lkl3kdo4gfm5k.apps.googleusercontent.com";
// from the API console
const client_secret = "WnGC6KJ91H40mg6H9r1eF9L";
// from https://developers.google.com/identity/protocols/OAuth2WebServer#offline
const refresh_url = "https://www.googleapis.com/oauth2/v4/token";
const post_body = `grant_type=refresh_token&client_id=${encodeURIComponent(client_id)}&client_secret=${encodeURIComponent(client_secret)}&refresh_token=${encodeURIComponent(refresh_token)}`;
let refresh_request = {
body: post_body,
method: "POST",
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded'
})
}
// post to the refresh endpoint, parse the json response and use the access token to call files.list
fetch(refresh_url, refresh_request).then( response => {
return(response.json());
}).then( response_json => {
console.log(response_json);
files_list(response_json.access_token);
});
}
// a quick and dirty function to list some Drive files using the newly acquired access token
function files_list (access_token) {
const drive_url = "https://www.googleapis.com/drive/v3/files";
let drive_request = {
method: "GET",
headers: new Headers({
Authorization: "Bearer "+access_token
})
}
fetch(drive_url, drive_request).then( response => {
return(response.json());
}).then( list => {
console.log("Found a file called "+list.files[0].name);
});
}
get_access_token_using_saved_refresh_token();
Warning May 2022 - this answer may not be valid any longer - see David Stein's comment
Let me add an alternative route to pinoyyid's excellent answer (which didn't work for me - popping redirect errors).
Instead of using the OAuthPlayground you can also use the HTTP REST API directly. So the difference to pinoyyid's answer is that we'll do things locally. Follow steps 1-3 from pinoyyid's answer. I'll quote them:
Create the Google Account (eg. my.drive.app#gmail.com) - Or skip this step if you are using an existing account.
Use the API console to register the mydriveapp (https://console.developers.google.com/apis/credentials/oauthclient?project=mydriveapp or just https://console.developers.google.com/apis/)
Create a new set of credentials (NB OAuth Client ID not Service Account Key and then choose "Web Application" from the selection)
Now, instead of the playground, add the following to your credentials:
Authorized JavaScript Sources: http://localhost (I don't know if this is required but just do it.)
Authorized Redirect URIs: http://localhost:8080
Screenshot (in German):
Make sure to actually save your changes via the blue button below!
Now you'll probably want to use a GUI to build your HTTP requests. I used Insomnia but you can go with Postman or plain cURL. I recommend Insomnia for it allows you to go through the consent screens easily.
Build a new GET request with the following parameters:
URL: https://accounts.google.com/o/oauth2/v2/auth
Query Param: redirect_uri=http://localhost:8080
Query Param: prompt=consent
Query Param: response_type=code
Query Param: client_id=<your client id from OAuth credentials>
Query Param: scope=<your chosen scopes, e.g. https://www.googleapis.com/auth/drive.file>
Query Param: access_type=offline
If your tool of choice doesn't handle URL encoding automagically make sure to get it right yourself.
Before you fire your request set up a webserver to listen on http://localhost:8080. If you have node and npm installed run npm i express, then create an index.js:
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('ok');
console.log(req)
});
app.listen(8080, function () {
console.log('Listening on port 8080!');
});
And run the server via node index.js. I recommend to either not log the whole req object or to run node index.js | less for the full output will be huge.
There are very simple solutions for other languages, too. E.g. use PHP's built in web server on 8080 php -S localhost:8080.
Now fire your request (in Insomnia) and you should be prompted with the login:
Log in with your email and password and confirm the consent screen (should contain your chosen scopes).
Go back to your terminal and check the output. If you logged the whole thing scroll down (e.g. pgdown in less) until you see a line with code=4/....
Copy that code; it is your authorization code that you'll want to exchange for an access and refresh token. Don't copy too much - if there's an ampersand & do not copy it or anything after. & delimits query parameters. We just want the code.
Now set up a HTTP POST request pointing to https://www.googleapis.com/oauth2/v4/token as form URL encoded. In Insomnia you can just click that - in other tools you might have to set the header yourself to Content-Type: application/x-www-form-urlencoded.
Add the following parameters:
code=<the authorization code from the last step>
client_id=<your client ID again>
client_secret=<your client secret from the OAuth credentials>
redirect_uri=http://localhost:8080
grant_type=authorization_code
Again, make sure that the encoding is correct.
Fire your request and check the output from your server. In the response you should see a JSON object:
{
"access_token": "xxxx",
"expires_in": 3600,
"refresh_token": "1/xxxx",
"scope": "https://www.googleapis.com/auth/drive.file",
"token_type": "Bearer"
}
You can use the access_token right away but it'll only be valid for one hour. Note the refresh token. This is the one you can always* exchange for a new access token.
* You will have to repeat the procedure if the user changes his password, revokes access, is inactive for 6 months etc.
Happy OAuthing!

How to reset google oauth 2.0 authorization?

I'm using Google APIs Client Library for JavaScript (Beta) to authorize user google account on web application (for youtube manipulations). Everything works fine, but i have no idea how to "logout" user from my application, i.e. reset access tokens.
For example, following code checks user authorization and if not, shows popup window for user to log into account and permit web-application access to user data:
gapi.auth.authorize({client_id: CLIENT_ID, scope: SCOPES, immediate: false}, handleAuth);
But client library doesn't have methods to reset authorization.
There is workaround to redirect user to "accounts.google.com/logout", but this
approach is not that i need: thus we logging user off from google account not only from my application, but also anywhere.
Google faq and client library description neither helpful.
Try revoking an access token, that should revoke the actual grant so auto-approvals will stop working. I assume this will solve your issue.
https://developers.google.com/accounts/docs/OAuth2WebServer#tokenrevoke
Its very simple. Just revoke the access.
void RevokeAcess()
{
try{
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("https://accounts.google.com/o/oauth2/revoke?token="+ACCESS_TOKEN);
org.apache.http.HttpResponse response = client.execute(post);
}
catch(IOException e)
{
}
}
But it should be in asyncTask
It depends what you mean by resetting authorization. I could think of a three ways of doing this:
Remove authorization on the server
Go to myaccount.google.com/permissions, find your app and remove it. The next time you try to sign in you have to complete full authorization flow with account chooser and consent screen.
Sign out on the client
gapi.auth2.getAuthInstance().signOut();
In this way Google authorization server still remembers your app and the authorization token remains in browser storage.
Sign out and disconnect
gapi.auth2.getAuthInstance().signOut();
gapi.auth2.getAuthInstance().disconnect();
This is equivalent to (1) but on the client.
Simply use: gapi.auth.setToken(null);
Solution for dotnet, call below API and pass the access token, doc - https://developers.google.com/identity/protocols/oauth2/web-server#tokenrevoke
string url = "https://accounts.google.com/o/oauth2/revoke?token=" + profileToken.ProfileAccessToken;
RestClient client = new RestClient(url);
var req = new RestRequest(Method.POST);
IRestResponse resp = client.Execute(req);

Resources