Search code examples
oauth-2.0imap

Oauth for imap, smtp and pop3 in long running background applications without user interaction


We sell a java app that is an extension for a web application that connects to a mailbox using IMAP, POP3 and SMTP. So far we've only supported basic auth (Username and Password) and encouraged users to use TLS and app passwords.

But now Microsoft is dropping support for basic auth on 1th of October in favor of Oauth2. Also Gmail is currently only supporting Oauth2. Which is why we need to add Oauth support to our app.

This faces us with multiple challenges as many of our customers put their servers behind firewalls so incoming and outgoing connections are often limited. Also since the app is distributed as a jar file we cannot pack the client secret into the app. Also since users use different servers the urls under which the server is reachable will vary.

I'm not really an expert on OAuth and I'm struggling with how to best approach this issue. We only have access to a web browser when the user does the initial configuration over the web ui. From there on the app has to be able to connect to the mailbox every 5 minutes without the possibility for user interaction.

On the surface Implicit Flow looks like the right choice for our use case because it doesn't require a client secret. But implicit flow doesn't support refresh tokens and I need a refresh token to be able to continuously get a new access token without user interaction.

So here are my questions:

  • Which OAuth flow would be the right fit for this use-case?
  • Is it ever acceptable to include the shared secret/private key in the jar file? As I understand the only downside would be the possibility of phishing which is an issue I cannot solve anyway.
  • How can I deal with different redirect urls for different customers?
  • Is it safe to rely on the refresh token to keep an app authenticated indefinitely?

Update

This is what I ended up doing:

For Gmail we let users register their own oAuth clients. This is necessary because Gmail doesn't provide dynamic client registration and device registration doesn't allow access to the gmail scope.

So we give users a UI to enter their client ID/client secret and a tutorial on how to create a client:

oAuth setup dialog

This requires a bit of extra legwork from our customers but since they have the technical skills to do so, this works.

This was the only way we got it to work without hacking the redirect url and including the client secret in the JAR.

So far bugcrowd researchers found nothing to complain.

Micorosoft Exchange on the other hand is very easy. Microsoft supports device flow with public clients. So we just use that.


Solution

  • Which OAuth flow would be the right fit for this use-case?

    I think you should implement the code flow. It's safer than implicit flow, and what is more, in the implicit flow you will not get a refresh token. If the user is using a browser to perform the initial configuration, then it should be possible to implement the code flow. Another flow that you can have a look at is the device flow. If your app is able to communicate the device code to the user, then the user can perform authentication on another browser, consent to the relevant permissions, and your app will pull the tokens from the Authorization Server.

    Is it ever acceptable to include the shared secret/private key in the jar file? As I understand the only downside would be the possibility of phishing which is an issue I cannot solve anyway.

    Not that it is not acceptable. It's more like — if you include the secret in the jar, then you can't treat it as a secret anymore. You should then treat your client as a public one and implement Proof-Key for Code Exchange in your code flow (PKCE). Another solution, probably a better one, is to use Dynamic Client Registration. When your application is first configured, it should register itself as a new client and get its own client ID and secret. This way you don't have to include it in your jar.

    How can I deal with different redirect urls for different customers?

    That's something that can be dealt with with Dynamic Client Registration. Your app can register itself with a new redirect URI, that will be used by the concrete instance. Otherwise, you would have to create a proxy, have one redirect URI, and in your proxy decide where you want to redirect the response, e.g. based on the client ID, state, or some other data.

    Is it safe to rely on the refresh token to keep an app authenticated indefinitely?

    If you implement DCR and subsequently use confidential clients, then yes, it's safe. Do keep in mind that refresh tokens might also have expiration times. So it still might be required to log in again.

    You can have a look at these resources if you need more information about the features mentioned here: