dovecot hosted in k8s, successfully authenticating to keycloak oidc and supporting “xoauth2” IMAP CAPABILITY
Nextcloud hosted in k8s with mail app enabled.
Problem:
When setting up Nextcloud Mail app to authenticate with the xoauth2 enabled IMAP dovecot server it does not attempt oauth authentication but rather explains that I’m logged in with a passwordless account and would first need to login with a password.
Question/Recommendation:
Why not allow xoauth2 authentication to any IMAP server? Dovecot would require the following IMAP commands to successfully login:
tag AUTHENTICATE XOAUTH2
{ base64 encoded data }
– The base64 encoded data is as follows:
—echo -en “user=emailAddress\001auth=Bearer token\001\001” | base64 -w0; echo
Evidence:
I use the same realm for Nextcloud and Dovecot clients in Keycloak. When I curl to acquire a new token from the Nextcloud client I assume I would then possess the same token that Nextcloud has after oidc Browser Flow login. With that token I can use the openssl s_client to successfully login to the IMAP server with XOAUTH2 capability using the Keycloak Dovecot client.
NOTES:
My method for retrieving the tokens from the two clients in the same keycloak realm were as follows:
It might be that the oidc_login app is requesting the claim_token_format https://openid.net/specs/openid-connect-core-1_0.html#IDToken instead of the urn:ietf:params:oauth:token-type:jwt. You can read about this in Authorization Services Guide.
Current line of thought; I’m thinking that I need to find a way to get oidc_login to use the access_token instead of the id_token. With this I would then have an access_token saved in session that I can then figure out how to pass in the mail app to the IMAP server. Here’s what I think I know so far.
I found this code which is an example of requesting a resource owner token:
use Jumbojett\OpenIDConnectClient;
$oidc = new OpenIDConnectClient('https://id.provider.com',
'ClientIDHere',
'ClientSecretHere');
$oidc->providerConfigParam(array('token_endpoint'=>'https://id.provider.com/connect/token'));
$oidc->addScope('my_scope');
//Add username and password
$oidc->addAuthParam(array('username'=>'<Username>'));
$oidc->addAuthParam(array('password'=>'<Password>'));
//Perform the auth and return the token (to validate check if the access_token property is there and a valid JWT) :
$token = $oidc->requestResourceOwnerToken(TRUE)->access_token;
in the README located at custom_apps/oidc_login/3rdparty/jumbojett/openid-connect-php/README.md under Example 5, however, I’m finding it difficult to determine if the oidc_login app is currently using this method of requesting a token. I think it is written differently somewhere in all of Nextcloud’s code.
So I still haven’t identified why Nextcloud doesn’t have the access_token.
Nextcloud has the built in Google xoauth2, why not allow it for other IMAP and SMTP servers?
Here’s a code snippet form the GoogleIntegration.php where it shows the use of grant_type authorization_code, how do I get this to work for my IMAP and SMTP server as well?
public function isGoogleOauthAccount(Account $account): bool {
return $account->getMailAccount()->getInboundHost() === 'imap.gmail.com'
&& $account->getMailAccount()->getAuthMethod() === 'xoauth2';
}
public function finishConnect(Account $account,
string $code): Account {
$clientId = $this->config->getAppValue(Application::APP_ID, 'google_oauth_client_id');
$encryptedClientSecret = $this->config->getAppValue(Application::APP_ID, 'google_oauth_client_secret');
if (empty($clientId) || empty($encryptedClientSecret)) {
// This is highly unexpected
$this->logger->critical('Can not finish Google account linking due to missing client secrets');
return $account;
}
$clientSecret = $this->crypto->decrypt($encryptedClientSecret);
$httpClient = $this->clientService->newClient();
try {
$response = $httpClient->post('https://oauth2.googleapis.com/token', [
'content-type' => 'application/json',
'body' => json_encode([
'client_id' => $clientId,
'client_secret' => $clientSecret,
'grant_type' => 'authorization_code',
'redirect_uri' => $this->getRedirectUrl(),
'code' => $code,
], JSON_THROW_ON_ERROR)
]);
} catch (Exception $e) {
$this->logger->error('Could not link Google account: ' . $e->getMessage(), [
'exception' => $e,
]);
return $account;
}
$data = json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR);
$encryptedRefreshToken = $this->crypto->encrypt($data['refresh_token']);
$account->getMailAccount()->setOauthRefreshToken($encryptedRefreshToken);
$encryptedAccessToken = $this->crypto->encrypt($data['access_token']);
$account->getMailAccount()->setOauthAccessToken($encryptedAccessToken);
$account->getMailAccount()->setOauthTokenTtl($this->timeFactory->getTime() + $data['expires_in']);
return $account;
}
I have not, I was hoping for someone that’s legitimately familiar with this section of the code to respond and say “Yea, that’s easy, give me 10 minutes”. My time is taken by another stage of my project so that I can continue making progress while waiting for a response on this part.
Very interested, I am in the exact same situation where I have ODIC working for Authentik to Nextcloud but Im not able to pass along that same token to other apps within Nextcloud.