diff --git a/.github/workflows/build-jar.yml b/.github/workflows/build-jar.yml
new file mode 100644
index 0000000..60674d5
--- /dev/null
+++ b/.github/workflows/build-jar.yml
@@ -0,0 +1,29 @@
+# This workflow will build a package using Maven and then publish it to GitHub packages when a release is created
+# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path
+
+name: gmail-oauth2-tools
+
+on: push
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ packages: write
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up JDK 11
+ uses: actions/setup-java@v3
+ with:
+ java-version: '11'
+ distribution: 'temurin'
+ server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
+ settings-path: ${{ github.workspace }}/java # location for the settings.xml file
+
+ - name: Build with Maven
+ run: mvn -B package --file pom.xml
+ - name: Test with Maven
+ run: mvn test --file pom.xml
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..64ee209
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+target/
+.vscode/
\ No newline at end of file
diff --git a/README.md b/README.md
index 6264733..5e07690 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+[](https://github.com/FreedomFaighter/gmail-oauth2-tools/actions/workflows/build-jar.yml)
+
Tools and sample code for authenticating to Gmail with OAuth2.
The specification is available [here](https://developers.google.com/gmail/xoauth2_protocol).
diff --git a/go/sendgmail/README.md b/go/sendgmail/README.md
index 6a70407..2bb91dd 100644
--- a/go/sendgmail/README.md
+++ b/go/sendgmail/README.md
@@ -22,8 +22,9 @@ send-email`.
* Follow the steps in the **Authorize credentials for a desktop
application** section. However, set the application type to *Web
application* (i.e. instead of *Desktop app*) and then add
- `https://oauth2.dance/` as an authorised redirect URI. This is necessary
- for seeing the authorisation code on a page in your browser.
+ `https://google.github.io/gmail-oauth2-tools/html/oauth2.dance.html`
+ as an authorised redirect URI. This is necessary for seeing the
+ authorisation code on a page in your browser.
* When you download the credentials as JSON, create the
`${XDG_CONFIG_HOME:-${HOME}/.config}/sendgmail` directory with file mode
diff --git a/html/oauth2.dance.html b/html/oauth2.dance.html
new file mode 100644
index 0000000..639d37a
--- /dev/null
+++ b/html/oauth2.dance.html
@@ -0,0 +1,30 @@
+
+
+
+
+
oauth2.dance
+
oauth2.dance
+
+ These aren't the droids you're looking for.
+
+
+ Authorisation Code:
+
+
+
+
+
diff --git a/java/build-java-sample-zip.sh b/java/build-java-sample-zip.sh
deleted file mode 100755
index 70fe685..0000000
--- a/java/build-java-sample-zip.sh
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/bin/sh
-#
-# Copyright 2012 Google Inc.
-#
-# Licensed 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.
-
-
-# Usage: build-java-sample-zip.sh [datestr]
-#
-# Copies the Java sample files to a temporary directory and zips them up.
-# The resulting ZIP file is left in /tmp.
-# If datestr is not specified, it defaults to the current date in YYYYMMDD
-# format; for example, "20120904". This would result in the zipfile
-# /tmp/oauth2-java-sample-20120904.zip.
-#
-# The script is intended to be run from the root of the Java code hierarchy.
-# It makes sure there are no local modifications to the files that are
-# being zipped up.
-
-
-top_level_files="README-java-sample.txt build.xml build.properties ../python/oauth2.py"
-relative_files="com/google/code/samples/oauth2/*.java"
-all_files="$top_level_files $relative_files"
-
-if [[ "$1" ]] ; then
- date="$1"
-else
- date=$(date "+%Y%m%d")
-fi
-
-relative_tmpdir="oauth2-java-sample-$date"
-full_tmpdir="/tmp/$relative_tmpdir"
-outfile="/tmp/oauth2-java-sample-$date.zip"
-
-if [[ -e $full_tmpdir ]]; then
- echo "ERROR: directory $full_tmpdir already exists"
- exit -1
-fi
-
-if [[ -e $outfile ]]; then
- echo "ERROR: $outfile already exists"
- exit -1
-fi
-
-status=$(svn status $all_files)
-if [[ "$status" ]] ; then
- echo "ERROR: One or more files has uncommitted changes:"
- echo "$status"
- exit -1
-fi
-
-mkdir -p $full_tmpdir
-cp $top_level_files $full_tmpdir
-cp --parents $relative_files $full_tmpdir
-
-cd /tmp
-zip -r $outfile $relative_tmpdir
-rm -r $full_tmpdir
-
-echo "Created $outfile"
diff --git a/java/build.properties b/java/build.properties
deleted file mode 100644
index 5eabffc..0000000
--- a/java/build.properties
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright 2012 Google Inc.
-#
-# Licensed 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.
-
-
-
-# If you are not running Java EE, you may need to supply a JAR file containing
-# the Java Mail package (see http://java.sun.com/products/javamail/).
-javamail_jar=
-
-# These are commandline parameters for OAuth2Authenticator.main().
-email=
-oauthToken=
-
-#
-# You shouldn't need to change anything below this point.
-#
-
-out=./out
-classes=./classes
-src=./
-
-# The generated JAR file will go here.
-oauth2_jar=${out}/oauth2.jar
diff --git a/java/build.xml b/java/build.xml
deleted file mode 100644
index 5ea1cd5..0000000
--- a/java/build.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/java/com/google/code/samples/oauth2/OAuth2Authenticator.java b/java/src/main/com/google/code/samples/oauth2/OAuth2Authenticator.java
similarity index 96%
rename from java/com/google/code/samples/oauth2/OAuth2Authenticator.java
rename to java/src/main/com/google/code/samples/oauth2/OAuth2Authenticator.java
index 596c5fc..254d7ad 100644
--- a/java/com/google/code/samples/oauth2/OAuth2Authenticator.java
+++ b/java/src/main/com/google/code/samples/oauth2/OAuth2Authenticator.java
@@ -13,7 +13,7 @@
* limitations under the License.
*/
-package com.google.code.samples.oauth2;
+package main.com.google.code.samples.oauth2;
import com.sun.mail.imap.IMAPStore;
import com.sun.mail.imap.IMAPSSLStore;
@@ -39,7 +39,7 @@ public class OAuth2Authenticator {
Logger.getLogger(OAuth2Authenticator.class.getName());
public static final class OAuth2Provider extends Provider {
- private static final long serialVersionUID = 1L;
+ private static final long serialVersionUID = 454502445710734267L;
public OAuth2Provider() {
super("Google OAuth2 Provider", 1.0,
@@ -147,7 +147,7 @@ public static void main(String args[]) throws Exception {
email,
oauthToken,
true);
- System.out.println("Successfully authenticated to IMAP.\n");
+ System.out.print("Successfully authenticated to IMAP.\n\n");
SMTPTransport smtpTransport = connectToSmtp("smtp.gmail.com",
587,
email,
diff --git a/java/com/google/code/samples/oauth2/OAuth2SaslClient.java b/java/src/main/com/google/code/samples/oauth2/OAuth2SaslClient.java
similarity index 70%
rename from java/com/google/code/samples/oauth2/OAuth2SaslClient.java
rename to java/src/main/com/google/code/samples/oauth2/OAuth2SaslClient.java
index 2bfef8d..43b1247 100644
--- a/java/com/google/code/samples/oauth2/OAuth2SaslClient.java
+++ b/java/src/main/com/google/code/samples/oauth2/OAuth2SaslClient.java
@@ -13,20 +13,20 @@
* limitations under the License.
*/
-package com.google.code.samples.oauth2;
+package main.com.google.code.samples.oauth2;
import java.io.IOException;
-import java.net.URISyntaxException;
import java.util.logging.Logger;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.sasl.SaslClient;
import javax.security.sasl.SaslException;
+import org.apache.maven.surefire.shared.lang3.NotImplementedException;
+
/**
* An OAuth2 implementation of SaslClient.
@@ -39,7 +39,8 @@ class OAuth2SaslClient implements SaslClient {
private final CallbackHandler callbackHandler;
private boolean isComplete = false;
-
+ private String MechanismName = "XOAUTH2";
+ private String Email;
/**
* Creates a new instance of the OAuth2SaslClient. This will ordinarily only
* be called from OAuth2SaslClientFactory.
@@ -50,8 +51,14 @@ public OAuth2SaslClient(String oauthToken,
this.callbackHandler = callbackHandler;
}
+ public OAuth2SaslClient()
+ {
+ this.oauthToken = null;
+ this.callbackHandler = null;
+ }
+
public String getMechanismName() {
- return "XOAUTH2";
+ return this.MechanismName;
}
public boolean hasInitialResponse() {
@@ -73,12 +80,12 @@ public byte[] evaluateChallenge(byte[] challenge) throws SaslException {
} catch (IOException e) {
throw new SaslException("Failed to execute callback: " + e);
}
- String email = nameCallback.getName();
+ this.Email = nameCallback.getName();
- byte[] response = String.format("user=%s\1auth=Bearer %s\1\1", email,
+ byte[] postToAppendToServerAddress = String.format("user=%s\1auth=Bearer %s\1\1", this.Email,
oauthToken).getBytes();
isComplete = true;
- return response;
+ return postToAppendToServerAddress;
}
public boolean isComplete() {
@@ -87,19 +94,43 @@ public boolean isComplete() {
public byte[] unwrap(byte[] incoming, int offset, int len)
throws SaslException {
- throw new IllegalStateException();
+ throw new NotImplementedException();
}
public byte[] wrap(byte[] outgoing, int offset, int len)
throws SaslException {
- throw new IllegalStateException();
+ throw new NotImplementedException();
}
public Object getNegotiatedProperty(String propName) {
if (!isComplete()) {
throw new IllegalStateException();
}
- return null;
+ switch(propName)
+ {
+ case "Email":
+ if(this.Email == null)
+ return null;
+ else
+ return this.Email;
+ case "MechanismName":
+ if(this.MechanismName == null)
+ return null;
+ else
+ return this.getMechanismName();
+ case "OAuthToken":
+ if(null == this.oauthToken)
+ return null;
+ else
+ return this.oauthToken;
+ case "CallbackHandler":
+ if(this.callbackHandler == null)
+ return null;
+ else
+ return this.callbackHandler;
+ default:
+ return null;
+ }
}
public void dispose() throws SaslException {
diff --git a/java/com/google/code/samples/oauth2/OAuth2SaslClientFactory.java b/java/src/main/com/google/code/samples/oauth2/OAuth2SaslClientFactory.java
similarity index 97%
rename from java/com/google/code/samples/oauth2/OAuth2SaslClientFactory.java
rename to java/src/main/com/google/code/samples/oauth2/OAuth2SaslClientFactory.java
index f399ad5..d39a974 100644
--- a/java/com/google/code/samples/oauth2/OAuth2SaslClientFactory.java
+++ b/java/src/main/com/google/code/samples/oauth2/OAuth2SaslClientFactory.java
@@ -13,7 +13,7 @@
* limitations under the License.
*/
-package com.google.code.samples.oauth2;
+package main.com.google.code.samples.oauth2;
import java.util.Map;
@@ -56,7 +56,7 @@ public SaslClient createSaslClient(String[] mechanisms,
return new OAuth2SaslClient((String) props.get(OAUTH_TOKEN_PROP),
callbackHandler);
}
-
+
public String[] getMechanismNames(Map props) {
return new String[] {"XOAUTH2"};
}
diff --git a/java/src/test/com/google/code/samples/oauth2/OAuth2AuthenticatorTests.java b/java/src/test/com/google/code/samples/oauth2/OAuth2AuthenticatorTests.java
new file mode 100644
index 0000000..bf6fd04
--- /dev/null
+++ b/java/src/test/com/google/code/samples/oauth2/OAuth2AuthenticatorTests.java
@@ -0,0 +1,34 @@
+package main.com.google.code.samples.oauth2;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.Assume.assumeTrue;
+import static org.junit.Assume.assumeFalse;
+import org.junit.jupiter.api.BeforeAll;
+
+import org.junit.jupiter.api.Test;
+
+import main.com.google.code.samples.oauth2.OAuth2Authenticator;
+
+import org.junit.jupiter.api.DisplayName;
+
+public class OAuth2AuthenticatorTests {
+
+ private final OAuth2Authenticator oAuth2Authenticator = new OAuth2Authenticator();
+
+ @DisplayName("Assumptions needed for tests")
+ @Test
+ void trueAssumption() {
+ assumeTrue((4 % 1) == 0);
+ assertEquals(8 % 3, 2);
+ assumeTrue((7 % 5) == 2);
+ assertEquals(7 % 3, 1);
+ assumeTrue(((2 + 2) % 2) == 0);
+ assumeFalse(((2 + 2) % 2) == 1);
+ assertEquals(2 + 2, 4);
+ }
+
+ @BeforeAll
+ static void initAll() {
+
+ }
+}
\ No newline at end of file
diff --git a/java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientFactoryTests.java b/java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientFactoryTests.java
new file mode 100644
index 0000000..d1db0a4
--- /dev/null
+++ b/java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientFactoryTests.java
@@ -0,0 +1,31 @@
+package main.pro.freemania.code.samples.oauth2;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import org.junit.jupiter.api.Test;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import main.com.google.code.samples.oauth2.OAuth2SaslClientFactory;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.DisplayName;
+import main.com.google.code.samples.oauth2.OAuth2SaslClient;
+
+public class OAuth2SaslClientFactoryTests {
+
+ private final OAuth2SaslClientFactory oAuth2SaslClientFactory = new OAuth2SaslClientFactory();
+
+ @BeforeAll
+ static void initAll() {
+
+ }
+
+ @Test
+ void getMechanismNameXOAUTH()
+ {
+ OAuth2SaslClient oAuth2SaslClient = new OAuth2SaslClient();
+ assertEquals(oAuth2SaslClient.getMechanismName(), oAuth2SaslClientFactory.getMechanismNames(null)[0]);
+ }
+}
\ No newline at end of file
diff --git a/java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientTests.java b/java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientTests.java
new file mode 100644
index 0000000..04343bc
--- /dev/null
+++ b/java/src/test/com/google/code/samples/oauth2/OAuth2SaslClientTests.java
@@ -0,0 +1,55 @@
+package main.pro.freemania.code.samples.oauth2;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.Assume.assumeTrue;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.apache.maven.surefire.api.testset.TestSetFailedException;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.DisplayName;
+import javax.security.auth.callback.CallbackHandler;
+import java.lang.reflect.Field;
+import main.com.google.code.samples.oauth2.OAuth2SaslClient;
+
+class OAuth2SaslClientTests {
+
+ private final OAuth2SaslClient oAuth2SaslClient = new OAuth2SaslClient();
+
+ @Test
+ public void oAuth2SaslClientgetNegotiatedPropertyThrows() throws TestSetFailedException {
+
+ String message = new String("propertyToSet");
+
+ assertThrows(IllegalStateException.class
+ , () -> {
+ oAuth2SaslClient.getNegotiatedProperty(message);
+ });
+ }
+
+ @Test
+ public void hasInitialResponseTest()
+ {
+ assertTrue(oAuth2SaslClient.hasInitialResponse());
+ }
+
+ public Field getPrivateFieldObject (String nameOfField) throws NoSuchFieldException
+ {
+ return OAuth2SaslClient.class.getDeclaredField(nameOfField);
+ }
+
+ @Test
+ public void getNegotiatedPropertyMechanismName() throws IllegalAccessException, NoSuchFieldException
+ {
+ Field f = getPrivateFieldObject("MechanismName");
+ f.setAccessible(true);
+ String name = (String)f.get(this.oAuth2SaslClient);
+ assertEquals(oAuth2SaslClient.getMechanismName(), name);
+ }
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..f8522f8
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,114 @@
+
+
+ 4.0.0
+ com.google.code.samples.oauth2
+ gmail-oauth2-tools
+ 1.0.1-SNAPSHOT
+ jar
+ https://github.com/FreedomFaighter/gmail-oauth2-tools
+ gmail-oauth2-tools
+ Tools and sample code for authenticating to Gmail with OAuth2.
+
+ UTF-8
+ UTF-8
+ 11
+
+
+
+
+
+ org.junit.platform
+ junit-platform-runner
+ 1.10.0
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.9.2
+ test
+
+
+ javax.mail
+ mail
+ 1.4.7
+
+
+ org.mockito
+ mockito-junit-jupiter
+ 5.4.0
+ test
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.1.2
+
+
+
+ java/src/main
+ java/src/test
+ ${project.basedir}/target
+ ${project.build.directory}/classes
+ ${project.artifactId}-${project.version}
+ ${project.build.directory}/test-classes
+
+
+ maven-compiler-plugin
+
+ 11
+ 11
+
+
+
+ maven-jar-plugin
+ 3.3.0
+
+
+ default-jar
+ package
+
+ jar
+
+
+
+
+
+
+ true
+ com.google.code.samples.oauth2.OAuth2Authenticator
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.1.2
+
+
+ **/*Tests.java
+
+
+
+
+
+ org.junit.platform
+ junit-platform-surefire-provider
+ 1.3.2
+
+
+
+ org.apache.maven.surefire
+ surefire-api
+ 3.1.2
+
+
+
+
+
+
\ No newline at end of file
diff --git a/python/oauth2.py b/python/oauth2.py
index eb14632..43f7b8e 100755
--- a/python/oauth2.py
+++ b/python/oauth2.py
@@ -22,7 +22,8 @@
registering and for documentation of the APIs invoked by this code.
NOTE: The OAuth2 OOB flow isn't a thing anymore. You will need to set the
-application type to "Web application" and then add https://oauth2.dance/ as an
+application type to "Web application" and then add
+https://google.github.io/gmail-oauth2-tools/html/oauth2.dance.html as an
authorised redirect URI. This is necessary for seeing the authorisation code on
a page in your browser.
@@ -134,7 +135,7 @@ def SetupOptionParser():
# Hardcoded redirect URI.
-REDIRECT_URI = 'https://oauth2.dance/'
+REDIRECT_URI = 'https://google.github.io/gmail-oauth2-tools/html/oauth2.dance.html'
def AccountsUrl(command):