This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-spring-boot-examples.git
The following commit(s) were added to refs/heads/main by this push: new 7858311 Added Microsoft Exchange IMAP Oauth2 Authentication example (#96) 7858311 is described below commit 78583119d6cec15a89b2727bbf15fb109442aedd Author: Luigi De Masi <5583341+luigidem...@users.noreply.github.com> AuthorDate: Tue Oct 25 20:59:58 2022 +0200 Added Microsoft Exchange IMAP Oauth2 Authentication example (#96) --- mail-ms-exchange-oauth2/Readme.adoc | 64 ++++++++++++ mail-ms-exchange-oauth2/pom.xml | 111 +++++++++++++++++++++ .../mail/oauth2/ApplicationConfiguration.java | 109 ++++++++++++++++++++ .../example/mail/oauth2/CamelApplication.java | 64 ++++++++++++ .../oauth2/Oauth2ExchangeMailAuthenticator.java | 82 +++++++++++++++ .../src/main/resources/application.properties | 43 ++++++++ pom.xml | 1 + 7 files changed, 474 insertions(+) diff --git a/mail-ms-exchange-oauth2/Readme.adoc b/mail-ms-exchange-oauth2/Readme.adoc new file mode 100644 index 0000000..f12c6ef --- /dev/null +++ b/mail-ms-exchange-oauth2/Readme.adoc @@ -0,0 +1,64 @@ +== Camel Spring Boot Microsoft Exchange IMAP Oauth2 Authentication + +This is an example that shows how to use Camel on Spring Boot to connect with +IMAP protocol and access email data for Office 365 users using OAuth2 authentication. + +The application will use the client credentials grant flow to get the access token +and use it to authenticate IMAP connections. + + +=== Prerequisite + +* To use OAuth, an application must be registered with Azure Active Directory. +Follow the instructions listed in https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app[Register an application with the Microsoft identity platform] to register a new application. + +* Enable application to access Exchange mailboxes via client credentials flow adding *IMAP.AccessAsApp* permission. Instructions https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth#add-the-pop-and-imap-permissions-to-your-aad-application[here] + +* Create and register your Azure Active Directory application's service principal +in Exchange via Exchange Online PowerShell. Instructions https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth#register-service-principals-in-exchange[here] + +* Edit the link:src/main/resources/application.properties[application.properties] adding the values to the properties marked with ``<FILL-ME>`` tag + +=== How to run + +You can run this example using: + +[source%nowrap, console] +---- +mvn spring-boot:run +---- +And you should see this output: + +[source%nowrap, console] +---- + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ + \\/ ___)| |_)| | | | | || (_| | ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | / / / / + =========|_|==============|___/=/_/_/_/ + :: Spring Boot :: (v2.7.5) + +2022-10-24 19:19:58.705 INFO 2274709 --- [ main] o.a.c.e.mail.oauth2.CamelApplication : Starting CamelApplication +2022-10-24 19:19:58.707 INFO 2274709 --- [ main] o.a.c.e.mail.oauth2.CamelApplication : No active profile set, falling back to 1 default profile: "default" +2022-10-24 19:20:00.777 INFO 2274709 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Apache Camel is starting +2022-10-24 19:20:00.785 INFO 2274709 --- [ main] c.s.b.CamelSpringBootApplicationListener : Starting CamelMainRunController to ensure the main thread keeps running +2022-10-24 19:20:00.785 INFO 2274709 --- [inRunController] org.apache.camel.main.MainSupport : Apache Camel (Main) is starting +2022-10-24 19:20:00.813 INFO 2274709 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Routes startup (started:1) +2022-10-24 19:20:00.813 INFO 2274709 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Started camel-mail-ms-exchange (imaps://outlook.office365.com:993) +2022-10-24 19:20:00.813 INFO 2274709 --- [ main] o.a.c.impl.engine.AbstractCamelContext : Apache Camel (CamelMailExchangeOAuth2) started in 1s226ms (build:29ms init:1s163ms start:34ms) +2022-10-24 19:20:00.819 INFO 2274709 --- [ main] o.a.c.e.mail.oauth2.CamelApplication : Started CamelApplication in 2.407 seconds (JVM running for 2.883) +2022-10-24 19:20:04.842 INFO 2274709 --- [fice365.com:993] camel-mail-ms-exchange : message Received: +From: John Doe <j...@example.com> +Subj: Test +Body: +This is a Test!!! + +2022-10-24 19:20:05.167 INFO 2274709 --- [fice365.com:993] camel-mail-ms-exchange : message Received: +From: Teigan Moore <tei...@xyz.com> +Subj: Prova +Body: +Prova Prova Prova +-- +Teigan Moore +---- \ No newline at end of file diff --git a/mail-ms-exchange-oauth2/pom.xml b/mail-ms-exchange-oauth2/pom.xml new file mode 100644 index 0000000..67b0b4b --- /dev/null +++ b/mail-ms-exchange-oauth2/pom.xml @@ -0,0 +1,111 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>examples</artifactId> + <groupId>org.apache.camel.springboot.example</groupId> + <version>3.20.0-SNAPSHOT</version> + </parent> + + <modelVersion>4.0.0</modelVersion> + + <artifactId>camel-example-spring-boot-mail-exchange-oauth2</artifactId> + <packaging>jar</packaging> + + <name>Camel SB Examples :: Mail :: Microsoft Exchange Oauth2 Authentication</name> + <description>An example showing how to use Camel on Spring Boot to connect + with IMAP protocol and access email data for Office 365 users using OAuth2 authentication</description> + + <properties> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + <msal4j-version>1.13.2</msal4j-version> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <dependencyManagement> + <dependencies> + <!-- Spring Boot BOM --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-dependencies</artifactId> + <version>${spring-boot-version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + <!-- Camel BOM --> + <dependency> + <groupId>org.apache.camel.springboot</groupId> + <artifactId>camel-spring-boot-bom</artifactId> + <version>${camel-version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + + <!-- Spring Boot --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter</artifactId> + </dependency> + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-validation</artifactId> + </dependency> + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-configuration-processor</artifactId> + <optional>true</optional> + </dependency> + + <!-- Camel --> + <dependency> + <groupId>org.apache.camel.springboot</groupId> + <artifactId>camel-spring-boot-starter</artifactId> + </dependency> + <dependency> + <groupId>org.apache.camel.springboot</groupId> + <artifactId>camel-mail-starter</artifactId> + </dependency> + + <!--Microsoft Authentication Library for Java --> + <dependency> + <groupId>com.microsoft.azure</groupId> + <artifactId>msal4j</artifactId> + <version>${msal4j-version}</version> + </dependency> + + </dependencies> + + + <build> + <!-- we do not want version in the JAR name --> + <finalName>${project.artifactId}</finalName> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <version>${spring-boot-version}</version> + <configuration> + <mainClass> + org.apache.camel.example.mail.oauth2.CamelApplication + </mainClass> + </configuration> + <executions> + <execution> + <goals> + <goal>repackage</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + +</project> \ No newline at end of file diff --git a/mail-ms-exchange-oauth2/src/main/java/org/apache/camel/example/mail/oauth2/ApplicationConfiguration.java b/mail-ms-exchange-oauth2/src/main/java/org/apache/camel/example/mail/oauth2/ApplicationConfiguration.java new file mode 100644 index 0000000..5828fa5 --- /dev/null +++ b/mail-ms-exchange-oauth2/src/main/java/org/apache/camel/example/mail/oauth2/ApplicationConfiguration.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.example.mail.oauth2; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.validation.annotation.Validated; + +import javax.validation.constraints.Email; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; + +@Validated +@Configuration +@ConfigurationProperties(prefix = "exchange") +public class ApplicationConfiguration { + + @NotEmpty + private String tenantId; + + @NotEmpty + private String clientId; + + @NotEmpty + private String clientSecret; + + @Email + @NotEmpty + private String user; + + @Min(1) + private long pollInterval = 60000; + + private boolean debug = false; + + private boolean delete = false; + + + + public String getTenantId() { + return tenantId; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getClientSecret() { + return clientSecret; + } + + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public long getPollInterval() { + return pollInterval; + } + + public void setPollInterval(long pollInterval) { + this.pollInterval = pollInterval; + } + + public boolean isDebug() { + return debug; + } + + public void setDebug(boolean debug) { + this.debug = debug; + } + + public boolean isDelete() { + return delete; + } + + public void setDelete(boolean delete) { + this.delete = delete; + } +} diff --git a/mail-ms-exchange-oauth2/src/main/java/org/apache/camel/example/mail/oauth2/CamelApplication.java b/mail-ms-exchange-oauth2/src/main/java/org/apache/camel/example/mail/oauth2/CamelApplication.java new file mode 100644 index 0000000..0e6a6a5 --- /dev/null +++ b/mail-ms-exchange-oauth2/src/main/java/org/apache/camel/example/mail/oauth2/CamelApplication.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.example.mail.oauth2; + +import org.apache.camel.LoggingLevel; +import org.apache.camel.builder.RouteBuilder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + + +@SpringBootApplication +@EnableConfigurationProperties +public class CamelApplication { + + @Autowired + ApplicationConfiguration conf; + + /** + * A main method to start this application. + */ + public static void main(String[] args) { + SpringApplication.run(CamelApplication.class, args); + } + + @Bean + public Oauth2ExchangeMailAuthenticator exchangeAuthenticator(){ + return new Oauth2ExchangeMailAuthenticator(conf.getTenantId(),conf.getClientId(), conf.getClientSecret(), conf.getUser()); + } + + @Bean + public RouteBuilder routeBuilder(){ + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("imaps://outlook.office365.com:993?" + + "delay="+conf.getPollInterval()+"}&" + + "authenticator=#exchangeAuthenticator&" + + "mail.imaps.auth.mechanisms=XOAUTH2&" + + "debugMode="+conf.isDebug()+"&" + + "delete="+conf.isDelete()+"&"+ + "bridgeErrorHandler=true") + .id("camel-mail-ms-exchange") + .log(LoggingLevel.INFO, "message Received: \nFrom: ${header.from}\nSubj: ${header.subject}\nBody:\n${body}"); + } + }; + } +} diff --git a/mail-ms-exchange-oauth2/src/main/java/org/apache/camel/example/mail/oauth2/Oauth2ExchangeMailAuthenticator.java b/mail-ms-exchange-oauth2/src/main/java/org/apache/camel/example/mail/oauth2/Oauth2ExchangeMailAuthenticator.java new file mode 100644 index 0000000..b0eac99 --- /dev/null +++ b/mail-ms-exchange-oauth2/src/main/java/org/apache/camel/example/mail/oauth2/Oauth2ExchangeMailAuthenticator.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.example.mail.oauth2; + +import com.microsoft.aad.msal4j.ClientCredentialFactory; +import com.microsoft.aad.msal4j.ClientCredentialParameters; +import com.microsoft.aad.msal4j.ConfidentialClientApplication; +import com.microsoft.aad.msal4j.IAuthenticationResult; +import com.microsoft.aad.msal4j.IClientCredential; +import org.apache.camel.component.mail.MailAuthenticator; + +import javax.mail.PasswordAuthentication; +import java.net.MalformedURLException; +import java.util.Collections; +import java.util.Set; + + +public class Oauth2ExchangeMailAuthenticator extends MailAuthenticator { + private static final Set<String> SCOPE = Collections.singleton("https://outlook.office365.com/.default"); + private static final String AUTHORITY_BASE_URL = "https://login.microsoftonline.com/"; + private final String clientId; + private final String clientSecret; + private final String authority; + private final String user; + + + public Oauth2ExchangeMailAuthenticator(String tenantId, String clientId, String clientSecret, String user) { + this.clientId = clientId; + this.clientSecret = clientSecret; + this.authority = AUTHORITY_BASE_URL + tenantId; + this.user = user; + } + + @Override + public PasswordAuthentication getPasswordAuthentication() { + String accessToken = acquireToken(); + return new PasswordAuthentication(user, accessToken); + } + + private String acquireToken() { + + ConfidentialClientApplication cca; + try { + // This is the secret that is created in the Azure portal when registering the application + IClientCredential credential = ClientCredentialFactory.createFromSecret(clientSecret); + + cca = ConfidentialClientApplication + .builder(clientId, credential) + .authority(authority) + .build(); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + + // Client credential requests will by default try to look for a valid token in the + // in-memory token cache. If found, it will return this token. If a token is not found, or the + // token is not valid, it will fall back to acquiring a token from the AAD service. Although + // not recommended unless there is a reason for doing so, you can skip the cache lookup + // by using .skipCache(true) in ClientCredentialParameters. + ClientCredentialParameters parameters = + ClientCredentialParameters + .builder(SCOPE) + .build(); + + IAuthenticationResult result = cca.acquireToken(parameters).join(); + return result.accessToken(); + } +} diff --git a/mail-ms-exchange-oauth2/src/main/resources/application.properties b/mail-ms-exchange-oauth2/src/main/resources/application.properties new file mode 100644 index 0000000..84446b0 --- /dev/null +++ b/mail-ms-exchange-oauth2/src/main/resources/application.properties @@ -0,0 +1,43 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +camel.springboot.name: CamelMailExchangeOAuth2 + +# Keep the application running +camel.springboot.main-run-controller: true + + +# Azure Active Directory tenant ID +exchange.tenantId=<FILL-ME> + +# Azure Active Directory client credentials +exchange.clientId=<FILL-ME> + +# Azure Active Directory client secret +exchange.clientSecret=<FILL-ME> + +# Microsoft Exchange Username. Must be an email. +exchange.user=<FILL-ME> + +# Milliseconds between two poll. +exchange.pollInterval=60000 + +# Enable javax.mail verbose output +exchange.debug=false + +# Delete message after consuming it +exchange.delete=false diff --git a/pom.xml b/pom.xml index b246620..9baca7d 100644 --- a/pom.xml +++ b/pom.xml @@ -58,6 +58,7 @@ <module>kafka-offsetrepository</module> <module>kamelet-chucknorris</module> <module>load-balancer-eip</module> + <module>mail-ms-exchange-oauth2</module> <module>master</module> <module>metrics</module> <module>opentracing</module>