Using IMAPSync to import from GMail - email

I am writing a Golang app to import emails from Gmail to a local email server using IMAPSync and Google's OAuth2.0. Lets call this the backend app.
There is also an accompanying ios app that uses AppAuth to enable to user login to their google account. With that i am able to get a refresh token, an access token, a JWT token with a payload that looks like
{
"iss": "accounts.google.com",
"at_hash": "HK6E_P6Dh8Y93mRNtsDB1Q",
"email_verified": "true",
"sub": "10769150350006150715113082367",
"azp": "1234987819200.apps.googleusercontent.com",
"email": "jsmith#example.com",
"aud": "1234987819200.apps.googleusercontent.com",
"iat": 1353601026,
"exp": 1353604926,
"nonce": "0394852-3190485-2490358",
"hd": "example.com"
}
The plan was to send this information to the backend and start the import in the backend with IMAPSync.
To login to the google account with IMAPSync i am running this command
/usr/bin/imapsync --host1 imap.gmail.com
--authmech1 xoauth2
--ssl1
--user1 user1#gmail.com
--password1 access_token
--host2 imap.gmail.com
--authmech1 xoauth2
--ssl1
--user2 user1#gmail.com
--password2 access_token
--justlogin --debug
Here are the last few lines of the output:
Host1: imap.gmail.com says it has CAPABILITY for AUTHENTICATE XOAUTH2
Use of uninitialized value $iss in concatenation (.) or string at /usr/bin/imapsync line 4463.
Use of uninitialized value $keyfile in concatenation (.) or string at /usr/bin/imapsync line 4463.
Use of uninitialized value $keyfile in concatenation (.) or string at /usr/bin/imapsync line 4466.
Service account:
Key file:
Key password: notasecret
pkcs12: Cannot open input file , No such file or directory
pkcs12: Use -help for summary.
Private key:
RSA.xs:288: OpenSSL error: no start line at /usr/share/perl5/JSON/WebToken/Crypt/RSA.pm line 19.
I am very unfamiliar with IMAP and email importing. Is this the right approach.
I followed this tutorial OAuth 2.0 for Mobile & Desktop Apps
Should i have followed this one (Using OAuth 2.0 for Web Server Applications) instead and have the backend do all the authentication and retrieve all the tokens?
But then how do i get the user to login as the ios app is the only User facing side of this application?
Thanks!

Read
https://imapsync.lamiral.info/FAQ.d/FAQ.XOAUTH2.txt
=======================================================================
Imapsync tips to use XOAUTH2 authentication (Gmail) and old XOAUTH
=======================================================================
=======================================================================
Q. Is XOAUTH2 authentication available with imapsync?
R. Yes, but XOAUTH2 has been really tested on Unix systems,
less profund on Windows but it should work.
Two file formats are available from Gmail: json and pk12.
json is easier to manage than pk12.
=======================================================================
Q. Imapsync XOAUTH2 fails with the following message, how to fix that?
{
"error": "unauthorized_client",
"error_description": "Unauthorized client or scope in request."
}
R. In order to work you also have to allow the service https://mail.google.com/
in the Google client API manager for OAUTH2.
"Select OAuth 2.0 scopes:"
=======================================================================
Q. How to use XOAUTH2 via a json file to globally authenticate gmail users?
R. Unless you use an imapsync binary like imapsync.exe or imapsync_bin_Darwin,
Perl modules needed for xoauth2 are:
Crypt::OpenSSL::RSA
JSON
JSON::WebToken
LWP
HTML::Entities
Encode::Byte
A easy way to install or upgrade Perl modules is to use cpanm command,
also called cpanminus. On Linux it is something like
sudo cpanm JSON::WebToken JSON Crypt::OpenSSL::RSA LWP HTML::Entities Encode::Byte
The json file patch code and explanation comes from Secretion at
https://github.com/imapsync/imapsync/pull/68
Here is a complete example for Gmail. It is a little stupid
since it is the same account as source and destination but
it's just to get the picture for xoauth2 authentication.
All xoauth2 config is given via the --password1 parameter.
It has the form:
--password1 secret.xoauth2.json
where secret.xoauth2.json is the json file given by Gmail.
imapsync \
--host1 imap.gmail.com --ssl1 --user1 gilles.lamiral#gmail.com \
--password1 secret.xoauth2.json --authmech1 XOAUTH2 \
--host2 imap.gmail.com --ssl2 --user2 gilles.lamiral#gmail.com \
--password2 secret.xoauth2.json --authmech2 XOAUTH2 \
--justlogin --debug
Use your own xoauth2 values.
The secret.xoauth2.json looks like:
{
"type": "service_account",
"project_id": "your-project-name",
"private_key_id": "1cfb..............................bd7fbe",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiGziM...ZV5ACKPHuOfp8A46I=\n-----END PRIVATE KEY-----\n",
"client_email": "jsonfile#your-project-name.iam.gserviceaccount.com",
"client_id": "105................689",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/jsonfile%40your-project-name.iam.gserviceaccount.com"
}
You get this json file by a link like:
https://console.developers.google.com/apis/credentials?project=your-project-name
See also:
https://developers.google.com/gmail/imap/xoauth2-protocol
https://developers.google.com/identity/protocols/OAuth2
=======================================================================
...

Related

outlook oauth 2.0 and IMAP

I'm trying to achieve the authentication for office 365 accounts by using oauth 2.0 ,
i'm using PublicClientApplication and InteractiveRequestParameters method for acquiring access token and in result successfully received access token ,refresh token and id token but when i am using access_token to connect to imap, i am getting error as authentication failure, can anyone please help me out what am i missing here.
have given all the required permissions from azure portal.
here is my code through which i am trying to connect to server
store.connect("outlook.office365.com", "my_email_id, access_token); //here store is imap
properties i have set are:
properties.put("mail.smtp.port", "587");
properties.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
properties.setProperty("mail.imap.socketFactory.fallback", "false");
properties.setProperty("mail.imap.port", "993");
properties.setProperty("mail.imap.socketFactory.port", "993");
properties.put("incomingHost", "outlook.office365.com");
properties.put("mail.imap.ssl.enable", "true");
properties.put("mail.smtp.starttls.enable", "true");
properties.put("outgoingHost", "smtp.office365.com");
properties.setProperty("mail.imaps.ssl.trust", "*");
Note: when i am providing password instead of access_token , i am successfully able to login but failure while using access_token.
Thank you !!
You cannot use your access token in place of a password. IMAP requires that your client use SASL XOAUTH2 to authenticate. Details can be found here.
For example, the SASL XOAUTH2 format to access test#contoso.onmicrosoft.com with access token EwBAAl3BAAUFFpUAo7J3Ve0bjLBWZWCclRC3EoAA is:
base64("user=test#contoso.onmicrosoft.com^Aauth=Bearer EwBAAl3BAAUFFpUAo7J3Ve0bjLBWZWCclRC3EoAA^A^A")
After base64 encoding, this translates to the following string. Note that line breaks are inserted for readability.
dXNlcj10ZXN0QGNvbnRvc28ub25taWNyb3NvZnQuY29tAWF1dGg9QmVhcmVy
IEV3QkFBbDNCQUFVRkZwVUFvN0ozVmUwYmpMQldaV0NjbFJDM0VvQUEBAQ==

Using Keycloak for defining subjects in policies in Eclispe Ditto

My current use case is: I have a frontend application where a user is logged in via Keycloak. I would like to implement some parts of the Ditto HTTP API in this frontend (https://www.eclipse.org/ditto/http-api-doc.html).
For example I want to create policies (https://www.eclipse.org/ditto/basic-policy.html) for authorization. I've read in the documentation that one can use an OpenID Connect compliant provider and the form is : (https://www.eclipse.org/ditto/basic-policy.html#who-can-be-addressed).
There's basic auth example at the bottom of the page, it seems to use the username in this case.
{
"policyId": "my.namespace:policy-a",
"entries": {
"owner": {
"subjects": {
"nginx:ditto": {
"type": "nginx basic auth user"
}
},
...
}
My question is: What exactly would be the sub-claim if I want to use Keycloak? Is it also the username of the user I want to grant rights to? And how would I get this in my frontend where I want to specify the policy for sending it to Ditto afterwards?
UPDATE 1:
I tried to enable keycloak authentication in Ditto like suggested below and as stated here: https://www.eclipse.org/ditto/installation-operating.html#openid-connect
Because I'm running Ditto with Docker Compose, I added the following line as an environment variable in ditto/deployment/docker/docker-compose.yml in line 136: - Dditto.gateway.authentication.oauth.openid-connect-issuers.keycloak=http://localhost:8090/auth/realms/twin
This URL is the same as in the issuer claim of my token which I'm receiving from keycloak.
Now if I try to make for example a post request with Postman to {{basePath}}/things I get the following error:
<html>
<head>
<title>401 Authorization Required</title>
</head>
<body bgcolor="white">
<center>
<h1>401 Authorization Required</h1>
</center>
<hr>
<center>nginx/1.13.12</center>
</body>
</html>
I chose Bearer Token as Auth in Postman and pasted a fresh token. Basic Auth with the default ditto user is still working.
Do I have to specify the new subject/my user in Ditto before?
UPDATE 2:
I managed to turn basic auth in nginx off by commenting out "auth_basic" and "auth_basic_user_file" in nginx.conf!
It seems to be forwarded to Ditto now, because now I get the following error with Postman:
{
"status": 401,
"error": "gateway:jwt.issuer.notsupported",
"message": "The JWT issuer 'localhost:8090/auth/realms/twin' is not supported.",
"description": "Check if your JWT is correct."
}
UPDATE 3:
My configuration in gateway.conf looks now like this:
oauth {
protocol = "http"
openid-connect-issuers = {
keycloak = "localhost:8090/auth/realms/twin"
}
}
I also tried to add these two lines in the docker-compose.yml:
- Dditto.gateway.authentication.oauth.protocol=http
- Dditto.gateway.authentication.oauth.openid-connect-issuers.keycloak=localhost:8090/auth/realms/twin
Unfortunately I still had no luck, same error as above :/ It seems like an user had a similar problem with keycloak before (https://gitter.im/eclipse/ditto?at=5de3ff186a85195b9edcb1a6), but sadly he mentioned no solution.
EDIT: It turns out that I specified these variables in the wrong way, the correct solution is to add them as part of command: java ... more info here
UPDATE 4:
I tried to build Ditto locally instead of using the latest docker images and I think I might be one step further now, it seems like my oauth config is working. I get now:
{
"status": 503,
"error": "gateway:publickey.provider.unavailable",
"message": "The public key provider is not available.",
"description": "If after retry it is still unavailable, please contact the service team."
}
The error message from the log is:
gateway_1 | 2020-11-05 15:33:18,669 WARN [] o.e.d.s.g.s.a.j.DittoPublicKeyProvider - Got Exception from discovery endpoint <http://localhost:8090/auth/realms/twin/.well-known/openid-configuration>.
gateway_1 | akka.stream.StreamTcpException: Tcp command [Connect(localhost:8090,None,List(),Some(10 seconds),true)] failed because of java.net.ConnectException: Connection refused
gateway_1 | Caused by: java.net.ConnectException: Connection refused
...
gateway_1 | java.util.concurrent.CompletionException: org.eclipse.ditto.services.gateway.security.authentication.jwt.PublicKeyProviderUnavailableException [message='The public key provider is not available.', errorCode=gateway:publickey.provider.unavailable, statusCode=SERVICE_UNAVAILABLE, description='If after retry it is still unavailable, please contact the service team.', href=null, dittoHeaders=ImmutableDittoHeaders [{}]]
...
gateway_1 | Caused by: org.eclipse.ditto.services.gateway.security.authentication.jwt.PublicKeyProviderUnavailableException [message='The public key provider is not available.', errorCode=gateway:publickey.provider.unavailable, statusCode=SERVICE_UNAVAILABLE, description='If after retry it is still unavailable, please contact the service team.', href=null, dittoHeaders=ImmutableDittoHeaders [{}]]
...
gateway_1 | Caused by: akka.stream.StreamTcpException: Tcp command [Connect(localhost:8090,None,List(),Some(10 seconds),true)] failed because of java.net.ConnectException: Connection refused
gateway_1 | Caused by: java.net.ConnectException: Connection refused
My keyloak is definitely running, I'm able to get tokens. If I'm opening http://localhost:8090/auth/realms/twin/.well-known/openid-configuration which is in the first error message, I'm able to see my openid-configuration from keycloak config.
Edit: It seems that my gateway container cannot reach my keycloak container, will try to figure this out.
FINAL UPDATE:
Unreachable keycloak docker container from the gateway docker container was the issue. I'm now using traefik:
Keycloak container has the following alias: keycloak.localhost
Oauth configuration in the gateway looks like this:
oauth {
protocol = "http"
openid-connect-issuers = {
keycloak = "keycloak.localhost/auth/realms/twin"
}
}
Now the gateway can find the keycloak container via the alias and I can still use the keycloak admin ui from my localhoast: http://keycloak.localhost:8090/auth/admin/
Additional info: Traefic Blog
What exactly would be the sub-claim if I want to use Keycloak?
Keycloak provides you a JWT.
A JWT is an encrypted JSON which contains multiple fields called "claims". You can check how your token looks like by visiting https://jwt.io and pasting your token there. One of those fields is called sub. This is the sub claim.
To enable your keycloak authentication in eclipse ditto you need to add the issuer to the ditto configuration.
An example can be founde here.
The address must match the URL in the issuer claim of your JWT token.
ditto.gateway.authentication {
oauth {
protocol = "http"
openid-connect-issuers = {
some-name = "localhost:8090/auth/realms/twin"
}
}
}
Is it also the username of the user I want to grant rights to?
In eclipse ditto there is not really a concept of "user names". Eclipse ditto authentication is based on authorization subjects. For the basic authentication example you provided, the authorization subject which is generated within ditto is nginx:ditto.
For JWT authentication the authorization subject is generated as a combination of the name for the open id connect issuer which you configured (in my case some-name) and the value of the sub claim. An authorization subject could look like this: some-name:8d078113-3ee5-4dbf-8db1-eb1a6cf0fe81.
And how would I get this in my frontend where I want to specify the policy for sending it to Ditto afterwards?
I'm not sure if I understand the question correctly. If you mean how to authenticate your frontend HTTP requests to eclipse ditto, you need to provide the JWT to eclipse ditto by adding it to the authorization header of your HTTP requests in the following form:
authorization: Bearer yourJWT
If you mean how you would know the sub claim of a JWT, you need to parse the JWT to a JSON object and then read the sub claim out of the payload section.

Why is the CouchDB JWT Authentication with Auth0 not working for me?

I have been trying to use the JWT authentication method in CouchDB together with Auth0. Following the Documentation at https://docs.couchdb.org/en/stable/api/server/authn.html#jwt-authentication , i added the authentication_handlers in my default.ini file and configured jwt_auth and jwt_keys like this:
[jwt_auth]
; List of claims to validate
required_claims = alg,kid,sub
[jwt_keys]
rsa:<kid> = -----BEGIN PUBLIC KEY-----\n
<PublicKey>
\n-----END PUBLIC KEY-----\n
I got the kid and the public key from Auth0 (https://[myDomain]/.well-known/jwks.json). Now when i request the JWT Token and add it to the CouchDB request against the _session endpoint i get the following response:
{
"ok": true,
"userCtx": {
"name": null,
"roles": []
},
"info": {
"authentication_handlers": [
"cookie",
"default"
]
}
}
I feel like i am doing something wrong within the default.ini file. Can anyone help me?
It looks like you haven't enabled JWT authentication in CouchDB. I say this because this:
"info": {
"authentication_handlers": [
"cookie",
"default"
]
}
should also include "jwt".
To get that, the [chttpd] block in CouchDB config should include:
{chttpd_auth, jwt_authentication_handler}
In addition, I don't think you need to have those three required claims listed. I got this working just the other day, and as those are required by Couch, you don't need to list them. Indeed, when I listed any one of them I got an error about "duplicate requirement" or something.
Also, make sure there's no iss (issuer) claim in the JWT. This is an issue likely to be fixed in a future release, but as of the time of writing, to get this to work with Couch 3.1 I had to explicitly make sure there was no iss claim.
More details in this issue.
So this is pretty embarrassing but my problem was that I didn't enter the public key in PEM format, so all i had to do was to use this command:
curl https://[myDomain]/pem -s | openssl x509 -pubkey -noout
to get the PEM format and use it to configure the CouchDB.

Robot Framework api test with OAUTH2 Authorization Request Header

I am trying to use the RequestsLibrary on an api thats using the OAUTH2 authentication.
Authentication is via OAUTH2 with credentials being supplied to the /v1/authtoken endpoint.
Subsequent calls to the APÍ need to have the token included as a ‘bearer’ in the ‘Authorization’ header of the http requests.
So below is the test case. The error I am getting is:
401 != 200
The credentials work ok in jmeter and a list of accounts is returned. However, I am not able to get the RF script work. Any help will be appreciated.
In the script,
Log to Console ${accessToken} returns the access token: 8ETFXTZOWQLrgsMj7c_KuCEeypdj-eO1r...
Log to Console ${token} returns: Bearer 8ETFXTZOWQLrgsMj7c_KuCEeypdj-eO1r...
*** Test Cases ***
Get authToken
Create Session hook http://xxxx.azurewebsites.net verify=${True}
${data}= Create Dictionary grant_type=client_credentials client_id=yyy-zzzz client_secret=xxxxxxxxxxxxxxx
${headers}= Create Dictionary Content-Type=application/x-www-form-urlencoded
${resp}= post request hook /v1/authtoken data=${data} headers=${headers}
Should Be Equal As Strings ${resp.status_code} 200
Dictionary Should Contain Value ${resp.json()} bearer
${accessToken}= evaluate $resp.json().get("access_token")
Log to Console ${accessToken}
${Bearer}= Set Variable Bearer
${token}= catenate Bearer ${accessToken}
Log to Console ${token}
${headers}= Create Dictionary Authorization=${token}
${resp1}= get request hook /v1/integration/accounts headers=${headers}
Should Be Equal As Strings ${resp1.status_code} 200
#Log to Console ${resp1.json()}
I am using OAuth 2.0 authentication as well for my salesforce automation.
My first answer would be to skip client based authentication and switch to username/password based authentication
Get authToken by Password Authentication
RequestsLibrary.Create Session hook https://<url>/services/oauth2 verify=${True}
${data}= Create Dictionary grant_type=password client_id=1abc client_secret=2abc username=test#test.com password=keypass
${headers}= Create Dictionary Content-Type=application/x-www-form-urlencoded
${resp}= RequestsLibrary.Post Request hook /token data=${data} headers=${headers}
Should Be Equal As Strings ${resp.status_code} 200
${accessToken}= evaluate $resp.json().get("access_token")
Log to Console ${accessToken}
If you are using client based or web based authentication, there will be a login screen that will be used by the user to enter their username/password to authorise the app to send requests on its behalf. Have a look at these pages for more information as they primarily discuss about using either refresh tokens or skipping the user prompt altogether.
Why does Google OAuth2 re-ask user for permission when i send them to auth url again
https://salesforce.stackexchange.com/questions/785/authenticate-3rd-party-application-with-oauth2
I have added the new answer for this question.
RequestsLibrary.Create Session OA2 <Your Server URL> verify=${True}
${data}= Create Dictionary Token_Name=TestTokenname grant_type=<grant type> client_Id=<your Id> Client_Secret=<Your client secret> scope=<your scpe>
${headers}= Create Dictionary Content-Type=application/x-www-form-urlencoded
${resp}= RequestsLibrary.Post Request OA2 identity/connect/token data=${data} headers=${headers}
BuiltIn.Log To Console ${resp}
BuiltIn.Log To Console ${resp.status_code}
Should Be Equal As Strings ${resp.status_code} 200
Dictionary Should Contain Value ${resp.json()} Testtokenname
${accessToken}= evaluate $resp.json().get("access_token")
BuiltIn.Log to Console ${accessToken}
${token}= catenate Bearer ${accessToken}
BuiltIn.Log to Console ${token}
${headers1}= Create Dictionary Authorization=${token}
RequestsLibrary.Create Session GT <Your Server URL> verify=${True}
${resp}= RequestsLibrary.Get Request GT <Your API URL> headers=${headers1}
Should Be Equal As Strings ${resp.status_code} 200

Error using Google two factor auth with mu4e & Gmail

I have been using Google 2 factor auth for a while, and have several applications configured. One of them is offlineimap (where I download the mail), but when I use mu4e to compose a message, I get the following error:
Sending failed: 534-5.7.9 Application-specific password required.
Learn more at 534-5.7.9
http://support.google.com/accounts/bin/answer.py?answer=185833
I have a ~/.authinfo.gpg configured (and it decrypts successfully manually), and my ~/.offlineimaprc calls get_password_emacs (the example I used can be found here).
I've even attempted to skip the gpg piece to see if it works, using my mu4e Google App Password directly in the ~/.offlineimaprc, but I end up with the same result.
My ~/.authinfo.gpg file: (decrypted here, sensitive info removed)
machine imap.gmail.com login me#gmail.com port 993 password GoogleAppPassword
machine smtp.gmail.com login me#gmail.com port 587 password GoogleAppPassword
My ~/.offlineimaprc file:
[general]
accounts = Gmail
maxsyncaccounts = 3
pythonfile = ~/.offlineimap.py
[Account Gmail]
localrepository = Local
remoterepository = Remote
[Repository Local]
type = Maildir
localfolders = ~/Maildir
[Repository Remote]
remotehost = imap.gmail.com
remoteuser = me#gmail.com
remotepasseval = get_password_emacs("imap.gmail.com", "me#gmail.com", "993")
ssl = yes
maxconnections = 1
realdelete = no
holdconnectionopen = true
keepalive = 60
type = IMAP
and my ~/.offlineimap.py
#!/usr/bin/python
import re, os
def get_password_emacs(machine, login, port):
s = "machine %s login %s port %s password ([^ ]*)\n" % (machine, login, port)
p = re.compile(s)
authinfo = os.popen("gpg -q --no-tty -d ~/.authinfo.gpg").read()
return p.search(authinfo).group(1)
Can anyone see the issue I'm having? I've validated that the ~/.authinfo.gpg file decrypts successfully, and that my Google App Password is correct.
Thanks for your time.
using my mu4e Google App Password directly in the ~/.offlineimaprc
That's exactly the problem. You shouldn't be using the password directly. For legacy applications that do not accept the second factor token, you need to use the application-specific password, instead. This is a password that you generate from this URL:
https://security.google.com/settings/security/apppasswords
And you use the generated password in lieue of your real password. You should note, however, that these application-specific passwords grant full access to your account. As a result, using app passwords significantly reduces the protections you get from enabling 2-factor on your account.