diff --git a/.github/workflows/maven-deploy.yml b/.github/_old-maven-deploy.yml
similarity index 100%
rename from .github/workflows/maven-deploy.yml
rename to .github/_old-maven-deploy.yml
diff --git a/.github/workflows/maven-deploy-release.yml b/.github/workflows/maven-deploy-release.yml
new file mode 100644
index 0000000..ed702c9
--- /dev/null
+++ b/.github/workflows/maven-deploy-release.yml
@@ -0,0 +1,45 @@
+name: Maven Deploy Release
+on:
+ release:
+ types: [published]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ services:
+ mysql:
+ image: mariadb:latest
+ env:
+ MYSQL_ALLOW_EMPTY_PASSWORD: yes
+ MYSQL_DATABASE: test
+ MYSQL_USER: test
+ MYSQL_PASSWORD: test
+ MYSQL_RANDOM_ROOT_PASSWORD: yes
+ ports:
+ - 3306
+ options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up JDK 1.8
+ uses: actions/setup-java@v1
+ with:
+ java-version: 1.8
+ - name: Build
+ run: mvn -B -DbuildVersion=${{ github.event.release.tag_name }} package -Dmaven.test.skip=true
+ - name: Test
+ run: mvn -B test
+ env:
+ MYSQL_PORT: ${{ job.services.mysql.ports[3306] }}
+ MYSQL_USERNAME: test
+ MYSQL_PASSWORD: test
+ - name: Install GPG Key
+ run: echo -e "$GPG_PRIVATE_KEY" | gpg --import --no-tty --batch --yes
+ env:
+ GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
+ - name: Deploy to JavaWebStack Repository
+ run: mvn deploy -B -DbuildVersion=${{ github.event.release.tag_name }} -s build/settings.xml -Dmaven.test.skip=true
+ env:
+ DEPLOYMENT_USERNAME: ${{ secrets.DEPLOYMENT_USERNAME }}
+ DEPLOYMENT_PASSWORD: ${{ secrets.DEPLOYMENT_PASSWORD }}
+ GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
+ OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
diff --git a/README.md b/README.md
index de479e6..c5d6322 100644
--- a/README.md
+++ b/README.md
@@ -15,20 +15,44 @@ Passport is a JWS-Module which allows you to create easily Authentication in you
### Example usage
```java
-class MyApp extends WebApplication {
+import org.javawebstack.passport.services.oauth2.InteraAppsOAuth2Service;
+
+class MyApp {
/* ... */
- protected void setupModules() {
+ protected void setup() {
+ HTTPServer httpServer = new HTTPServer().port(1234);
+
+ Passport passport = new Passport("/auth");
+ OAuth2Strategy oAuth2Strategy = new OAuth2Strategy("http://localhost:1234");
+ oAuth2Strategy.setHttpCallbackHandler((e, callback) -> {
+ return "Hello " + callback.getProfile().getName();
+ });
+
+ oAuth2Strategy.use("interaapps", new InteraAppsOAuth2Provider("myid", "mysecret").setScopes("user:read"));
+
+ passport.use("oauth2", oAuth2Strategy);
- OAuth2Module oAuth2Module = new OAuth2Module();
- oAuth2Module
- .addService(new GithubOAuth2Service("", "", /*Redirect Host*/ "http://localhost:2222"))
- .setOAuth2Callback((service, exchange, callback) -> {
- System.out.println("Someone logged in with "+service);
- return "Hello "+callback.getProfile().name;
- });
- addModule(oAuth2Module);
+ passport.createRoutes(httpServer);
+ httpServer.start();
+
+ // Creates Routes: /auth/oauth2/interaapps, /auth/oauth2/interaapps/callback
}
+
+ // JWS-Passport ships also an abstracted form of handling oauth2
+ public void oAuthWithoutHTTPServer() {
+ OAuth2Strategy oAuth2Strategy = new OAuth2Strategy("http://localhost:1234");
+ oAuth2Strategy.use("interaapps", new InteraAppsOAuth2Provider("myid", "mysecret").setScopes("user:read"));
+
+ // Redirect
+ String callbackUrl = ".../callback";
+ String redirectUrl = oAuth2Strategy.get("interaapps").redirect(callbackUrl);
+
+ // On callback
+ OAuth2Callback callback = oAuth2Strategy.get("interaapps").callback(new AbstractObject().set("code", code), callbackUrl);
+ System.out.println("Hello "+callback.getProfile().name);
+ }
+
/* ... */
}
```
@@ -41,19 +65,6 @@ class MyApp extends WebApplication {
1.0-SNAPSHOT
```
-#### or Jitpack
-```xml
-
- jitpack.io
- https://jitpack.io
-
-
-
- com.github.JavaWebStack
- Passport
- COMMIT_HASH
-
-```
# Services
Service|Class|Control-Panel|More Information
@@ -63,4 +74,4 @@ Google|GoogleOAuth2Service|[Google Developer Console](https://console.developers
Discord|DiscordOAuth2Service|[Discord Developer Portal](https://discord.com/developers/applications)|-
Facebook|FacebookOAuth2Service|[Facebook Developer Center](https://console.developers.google.com/)|TODO
InteraApps|InteraAppsOAuth2Service|[IA-Accounts Developer Center](https://accounts.interaapps.de/developers/projects)|-
-Twitch|TwitchOAuth2Service|[Twitch Developers](https://dev.twitch.tv/)|Implements the OAuth authorization code flow
\ No newline at end of file
+Twitch|TwitchOAuth2Service|[Twitch Developers](https://dev.twitch.tv/)|Implements the OAuth authorization code flow
diff --git a/pom.xml b/pom.xml
index 9f7ba19..01cd41f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,32 +4,52 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
+ Passport
+ Adds authentication with ease
+ https://github.com/JavaWebStack/passport
+
org.javawebstack
Passport
- 1.0-SNAPSHOT
+ ${buildVersion}
8
8
+ 1.0.1-SNAPSHOT
-
-
- javawebstack
- https://repo.javawebstack.org
-
-
+
+
+ The Apache License, Version 2.0
+ https://www.apache.org/licenses/LICENSE-2.0.txt
+
+
+
+
+
+ Julian Gojani
+ julian@gojani.xyz
+ JavaWebStack
+ https://javawebstack.org
+
+
+
+
+ scm:git:git://github.com/JavaWebStack/passport.git
+ scm:git:ssh://github.com:JavaWebStack/passport.git
+ https://github.com/JavaWebStack/passport/tree/master
+
org.javawebstack
- Web-Framework
- 1.0-SNAPSHOT
+ http-client
+ 1.0.0
org.javawebstack
- HTTP-Client
- 1.0-SNAPSHOT
+ http-server
+ 1.0.0
com.google.apis
@@ -46,12 +66,17 @@
- org.apache.maven.plugins
- maven-compiler-plugin
-
- 8
- 8
-
+ maven-deploy-plugin
+ 3.0.0-M1
+
+
+ default-deploy
+ deploy
+
+ deploy
+
+
+
org.apache.maven.plugins
@@ -59,15 +84,51 @@
2.22.1
- maven-deploy-plugin
- 3.0.0-M1
+ org.apache.maven.plugins
+ maven-source-plugin
+ 2.2.1
- default-deploy
- deploy
+ attach-sources
- deploy
+ jar-no-fork
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.9.1
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 3.0.1
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+ A313520526A8DFE1C2A30399C35A3D43C557B112
+ gpg
+
+ --no-tty
+ --batch
+ --yes
+
+
@@ -76,12 +137,12 @@
- javawebstack-snapshots
- https://nexus.lumaserv.cloud/repository/javawebstack-snapshots
+ ossrh
+ https://s01.oss.sonatype.org/content/repositories/snapshots
- javawebstack-releases
- https://nexus.lumaserv.cloud/repository/javawebstack-releases
+ ossrh
+ https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
-
\ No newline at end of file
+
diff --git a/src/main/java/org/javawebstack/passport/AuthService.java b/src/main/java/org/javawebstack/passport/AuthService.java
deleted file mode 100644
index 97f56db..0000000
--- a/src/main/java/org/javawebstack/passport/AuthService.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.javawebstack.passport;
-
-import org.javawebstack.httpserver.HTTPServer;
-
-public interface AuthService {
- String getName();
-
- default void setupServer(HTTPServer server){}
-}
diff --git a/src/main/java/org/javawebstack/passport/OAuth2Module.java b/src/main/java/org/javawebstack/passport/OAuth2Module.java
deleted file mode 100644
index e821ca5..0000000
--- a/src/main/java/org/javawebstack/passport/OAuth2Module.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package org.javawebstack.passport;
-
-import org.javawebstack.framework.WebApplication;
-import org.javawebstack.framework.module.Module;
-import org.javawebstack.httpserver.HTTPServer;
-import org.javawebstack.orm.exception.ORMConfigurationException;
-import org.javawebstack.orm.wrapper.SQL;
-import org.javawebstack.passport.services.oauth2.OAuth2Callback;
-import org.javawebstack.passport.services.oauth2.OAuth2CallbackHandler;
-import org.javawebstack.passport.services.oauth2.OAuth2Service;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class OAuth2Module implements Module {
-
- private final List services;
- private String pathPrefix = "/authorization/oauth2/";
-
- private OAuth2CallbackHandler oAuth2Callback = (service, exchange, callback)->null;
-
- public OAuth2Module(){
- services = new ArrayList<>();
- }
-
- public void beforeSetupServer(WebApplication application, HTTPServer server) {
- services.forEach(service -> {
- service.setupServer(server);
- if (service instanceof OAuth2Service) {
- server.get(pathPrefix+service.getName(), exchange -> ((OAuth2Service) service).redirect(exchange, this));
- server.get(pathPrefix+service.getName()+"/callback", exchange -> {
- OAuth2Callback callback = ((OAuth2Service) service).callback(exchange, this);
-
- return oAuth2Callback.callback(service.getName(), exchange, callback);
- });
- }
- });
- }
-
- public void setupModels(WebApplication application, SQL sql) throws ORMConfigurationException {
-
- }
-
- public OAuth2Module addService(AuthService authService) {
- services.add(authService);
- return this;
- }
-
- public List getServices() {
- return services;
- }
-
- public void setOAuth2Callback(OAuth2CallbackHandler oAuth2Callback) {
- this.oAuth2Callback = oAuth2Callback;
- }
-
- public OAuth2Module setPathPrefix(String pathPrefix) {
- this.pathPrefix = pathPrefix;
- return this;
- }
-
- public String getPathPrefix() {
- return pathPrefix;
- }
-}
diff --git a/src/main/java/org/javawebstack/passport/Passport.java b/src/main/java/org/javawebstack/passport/Passport.java
new file mode 100644
index 0000000..0cd5242
--- /dev/null
+++ b/src/main/java/org/javawebstack/passport/Passport.java
@@ -0,0 +1,42 @@
+package org.javawebstack.passport;
+
+import org.javawebstack.httpserver.HTTPServer;
+import org.javawebstack.passport.strategies.Strategy;
+import org.javawebstack.passport.strategies.oauth2.OAuth2Provider;
+import org.javawebstack.passport.strategies.oauth2.OAuth2Strategy;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Passport {
+
+ private Map strategies = new HashMap<>();
+ private String prefixUrl = "";
+
+ public Passport() {}
+
+ public Passport(String prefixUrl){
+ this.prefixUrl = prefixUrl;
+ }
+
+ public Passport use(String name, Strategy strategy){
+ strategy.setPassport(this);
+ strategies.put(name, strategy);
+ return this;
+ }
+
+ public Strategy get(String name){
+ return strategies.get(name);
+ }
+
+ public Passport createRoutes(HTTPServer server){
+ strategies.forEach((name, strategy) -> {
+ strategy.createRoutes(prefixUrl+"/"+name, server);
+ });
+ return this;
+ }
+
+ public String getPrefixUrl() {
+ return prefixUrl;
+ }
+}
diff --git a/src/main/java/org/javawebstack/passport/models/PassportUser.java b/src/main/java/org/javawebstack/passport/models/PassportUser.java
deleted file mode 100644
index 42dabe4..0000000
--- a/src/main/java/org/javawebstack/passport/models/PassportUser.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package org.javawebstack.passport.models;
-
-import org.javawebstack.orm.Model;
-import org.javawebstack.orm.annotation.Column;
-import org.javawebstack.orm.annotation.Dates;
-
-import java.sql.Timestamp;
-
-public interface PassportUser {
- String setPassword(String unhashedPassword);
- boolean checkPassword(String hashedPassword);
-}
diff --git a/src/main/java/org/javawebstack/passport/services/oauth2/DiscordOAuth2Service.java b/src/main/java/org/javawebstack/passport/services/oauth2/DiscordOAuth2Service.java
deleted file mode 100644
index e1f261d..0000000
--- a/src/main/java/org/javawebstack/passport/services/oauth2/DiscordOAuth2Service.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package org.javawebstack.passport.services.oauth2;
-
-import com.google.gson.Gson;
-import org.javawebstack.abstractdata.AbstractObject;
-import org.javawebstack.abstractdata.util.QueryString;
-import org.javawebstack.httpclient.HTTPClient;
-import org.javawebstack.httpserver.Exchange;
-import org.javawebstack.passport.OAuth2Module;
-import org.javawebstack.passport.Profile;
-
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-
-
-public class DiscordOAuth2Service extends HTTPClient implements OAuth2Service {
- private final String clientId;
- private final String secret;
- private String[] scopes = new String[]{"email", "identify"};
- private String redirectDomain;
-
- public DiscordOAuth2Service(String clientId, String secret, String redirectDomain){
- setBaseUrl("https://discord.com");
- this.clientId = clientId;
- this.secret = secret;
- this.redirectDomain = redirectDomain;
- }
-
- public String getName() {
- return "discord";
- }
-
- public DiscordOAuth2Service setScopes(String[] scopes) {
- this.scopes = scopes;
- return this;
- }
-
- public OAuth2Callback callback(Exchange exchange, OAuth2Module oAuth2Module) {
- AbstractObject abstractObject = post("/api/oauth2/token")
- .header("User-Agent", "JWSPassportClient/1")
- .formBodyString(new QueryString()
- .set("client_id", clientId)
- .set("client_secret", secret)
- .set("code", exchange.rawRequest().getParameter("code"))
- .set("grant_type", "authorization_code")
- .set("redirect_uri", redirectDomain+oAuth2Module.getPathPrefix()+getName()+"/callback")
- .set("scope", String.join(" ", scopes))
- .toString()
- )
- .data().object();
-
- return new OAuth2Callback(
- abstractObject.get("access_token").string(),
- getProfile(abstractObject.get("access_token").string()),
- new HTTPClient("https://discord.com/").bearer(abstractObject.get("access_token").string())).setRefreshToken(abstractObject.get("refresh_token").string());
- }
-
-
- public Object redirect(Exchange exchange, OAuth2Module oAuth2Module) {
- try {
- exchange.redirect("https://discord.com/api/oauth2/authorize?response_type=code&client_id="+clientId+"&prompt=consent&scope="+ URLEncoder.encode(String.join(" ", scopes), "UTF-8")+"&redirect_uri="+URLEncoder.encode(redirectDomain+oAuth2Module.getPathPrefix()+getName()+"/callback", "UTF-8"));
-
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- return null;
- }
- return "";
- }
-
- @Override
- public Profile getProfile(String accessToken) {
- Profile profile = new Profile();
-
- AbstractObject data =
- get("/api/users/@me")
- .bearer(accessToken)
- .header("User-Agent", "JWSPassportClient/1")
- .data().object();
-
- if (data.has("id")) {
- profile.id = data.get("id").string();
-
- if (data.has("avatar"))
- profile.avatar = "https://cdn.discordapp.com/avatars/"+profile.id+"/"+data.get("avatar").string()+".png";
- }
-
- if (data.has("username"))
- profile.name = data.get("username").string();
-
- if (data.has("email"))
- profile.mail = data.get("email").string();
-
- data.forEach(profile::set);
-
- return profile;
- }
-
-
-}
diff --git a/src/main/java/org/javawebstack/passport/services/oauth2/FacebookOAuth2Service.java b/src/main/java/org/javawebstack/passport/services/oauth2/FacebookOAuth2Service.java
deleted file mode 100644
index d68212e..0000000
--- a/src/main/java/org/javawebstack/passport/services/oauth2/FacebookOAuth2Service.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package org.javawebstack.passport.services.oauth2;
-
-import org.javawebstack.abstractdata.AbstractObject;
-import org.javawebstack.abstractdata.util.QueryString;
-import org.javawebstack.httpclient.HTTPClient;
-import org.javawebstack.httpserver.Exchange;
-import org.javawebstack.httpserver.helper.MimeType;
-import org.javawebstack.passport.OAuth2Module;
-import org.javawebstack.passport.Profile;
-
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-
-
-public class FacebookOAuth2Service extends HTTPClient implements OAuth2Service {
- private final String clientId;
- private final String secret;
- private String[] scopes = new String[]{"read:user","user:email"};
- private String redirectDomain;
-
- public FacebookOAuth2Service(String clientId, String secret, String redirectDomain){
- setBaseUrl("https://api.github.com");
- this.clientId = clientId;
- this.secret = secret;
- this.redirectDomain = redirectDomain;
- }
-
- public String getName() {
- return "github";
- }
-
- public FacebookOAuth2Service setScopes(String[] scopes) {
- this.scopes = scopes;
- return this;
- }
-
- public OAuth2Callback callback(Exchange exchange, OAuth2Module oAuth2Module) {
-
- AbstractObject abstractObject = new HTTPClient("https://github.com").post("/login/oauth/access_token")
- .formBodyString(new QueryString()
- .set("client_id", clientId)
- .set("client_secret", secret)
- .set("code", exchange.rawRequest().getParameter("code"))
- .toString())
- .header("Accept", MimeType.JSON.getMimeTypes().get(0))
- .data().object();
-
-
- if (abstractObject.has("scope")/* && abstractObject.get("scope").string().equals("read:user,user:email")*/) {
- Profile profile = new Profile();
- return new OAuth2Callback(abstractObject.get("access_token").string(), getProfile(abstractObject.get("access_token").string()), new HTTPClient("https://api.github.com").header("Authorization", "token "+abstractObject.get("access_token").string()));
- }
-
- return null;
- }
-
-
- public Object redirect(Exchange exchange, OAuth2Module oAuth2Module) {
- try {
- exchange.redirect("https://www.facebook.com/v11.0/dialog/oauth?client_id="+clientId+"&scope="+ URLEncoder.encode(String.join(" ", scopes), "UTF-8")+"&redirect_uri="+URLEncoder.encode(redirectDomain+oAuth2Module.getPathPrefix()+getName()+"/callback", "UTF-8"));
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- return null;
- }
- return "";
- }
-
- @Override
- public Profile getProfile(String accessToken) {
- Profile profile = new Profile();
- AbstractObject userData = get("/user")
- .header("Authorization", "token "+accessToken)
- .data().object();
-
- if (userData.has("id"))
- profile.id = userData.get("id").number().toString();
- if (userData.has("name"))
- profile.name = userData.get("name").string();
- if (userData.has("avatar_url"))
- profile.avatar = userData.get("avatar_url").string();
-
- userData.forEach(profile::set);
-
- get("/user/emails")
- .header("Authorization", "token "+accessToken)
- .data().array().forEach(abstractElement -> {
- if (profile.mail == null) {
- profile.mail = abstractElement.object().get("email").string();
- }
- });
- return profile;
- }
-
-
-}
diff --git a/src/main/java/org/javawebstack/passport/services/oauth2/GithubOAuth2Service.java b/src/main/java/org/javawebstack/passport/services/oauth2/GithubOAuth2Service.java
deleted file mode 100644
index f06f508..0000000
--- a/src/main/java/org/javawebstack/passport/services/oauth2/GithubOAuth2Service.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package org.javawebstack.passport.services.oauth2;
-
-import org.javawebstack.abstractdata.AbstractObject;
-import org.javawebstack.abstractdata.util.QueryString;
-import org.javawebstack.httpclient.HTTPClient;
-import org.javawebstack.httpserver.Exchange;
-import org.javawebstack.httpserver.helper.MimeType;
-import org.javawebstack.passport.OAuth2Module;
-import org.javawebstack.passport.Profile;
-
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-
-
-public class GithubOAuth2Service extends HTTPClient implements OAuth2Service {
- private final String clientId;
- private final String secret;
- private String[] scopes = new String[]{"read:user","user:email"};
- private String redirectDomain;
-
- public GithubOAuth2Service(String clientId, String secret, String redirectDomain){
- setBaseUrl("https://api.github.com");
- this.clientId = clientId;
- this.secret = secret;
- this.redirectDomain = redirectDomain;
- }
-
- public String getName() {
- return "github";
- }
-
- public GithubOAuth2Service setScopes(String[] scopes) {
- this.scopes = scopes;
- return this;
- }
-
- public OAuth2Callback callback(Exchange exchange, OAuth2Module oAuth2Module) {
-
- AbstractObject abstractObject = new HTTPClient("https://github.com").post("/login/oauth/access_token")
- .formBodyString(new QueryString()
- .set("client_id", clientId)
- .set("client_secret", secret)
- .set("code", exchange.rawRequest().getParameter("code"))
- .toString())
- .header("Accept", MimeType.JSON.getMimeTypes().get(0))
- .data().object();
-
-
- if (abstractObject.has("scope")/* && abstractObject.get("scope").string().equals("read:user,user:email")*/) {
- Profile profile = new Profile();
- return new OAuth2Callback(abstractObject.get("access_token").string(), getProfile(abstractObject.get("access_token").string()), new HTTPClient("https://api.github.com").authorization("token", abstractObject.get("access_token").string()));
- }
-
- return null;
- }
-
-
- public Object redirect(Exchange exchange, OAuth2Module oAuth2Module) {
- try {
- exchange.redirect("https://github.com/login/oauth/authorize?client_id="+clientId+"&scope="+ URLEncoder.encode(String.join(" ", scopes), "UTF-8")+"&redirect_uri="+URLEncoder.encode(redirectDomain+oAuth2Module.getPathPrefix()+getName()+"/callback", "UTF-8"));
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- return null;
- }
- return "";
- }
-
- @Override
- public Profile getProfile(String accessToken) {
- Profile profile = new Profile();
- AbstractObject userData = get("/user")
- .header("Authorization", "token "+accessToken)
- .data().object();
-
- if (userData.has("id"))
- profile.id = userData.get("id").number().toString();
- if (userData.has("name"))
- profile.name = userData.get("name").string();
- if (userData.has("avatar_url"))
- profile.avatar = userData.get("avatar_url").string();
-
- userData.forEach(profile::set);
-
- get("/user/emails")
- .authorization("token", accessToken)
- .data().array().forEach(abstractElement -> {
- if (profile.mail == null) {
- profile.mail = abstractElement.object().get("email").string();
- }
- });
- return profile;
- }
-
-
-}
diff --git a/src/main/java/org/javawebstack/passport/services/oauth2/GoogleOAuth2Service.java b/src/main/java/org/javawebstack/passport/services/oauth2/GoogleOAuth2Service.java
deleted file mode 100644
index c02b82c..0000000
--- a/src/main/java/org/javawebstack/passport/services/oauth2/GoogleOAuth2Service.java
+++ /dev/null
@@ -1,122 +0,0 @@
-package org.javawebstack.passport.services.oauth2;
-
-import com.google.api.client.auth.oauth2.Credential;
-import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
-import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
-import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;
-import com.google.api.client.http.javanet.NetHttpTransport;
-import com.google.api.client.json.gson.GsonFactory;
-import com.google.api.services.oauth2.Oauth2;
-import com.google.api.services.oauth2.model.Userinfo;
-import org.javawebstack.httpclient.HTTPClient;
-import org.javawebstack.httpserver.Exchange;
-import org.javawebstack.passport.OAuth2Module;
-import org.javawebstack.passport.Profile;
-
-import java.io.IOException;
-import java.util.Arrays;
-
-
-public class GoogleOAuth2Service extends HTTPClient implements OAuth2Service {
- private final String clientId;
- private final String secret;
- private String redirectDomain;
- private GoogleAuthorizationCodeFlow googleAuthorizationCodeFlow;
-
- public GoogleOAuth2Service(String clientId, String secret, String redirectDomain){
- setBaseUrl("https://api.github.com");
- this.clientId = clientId;
- this.secret = secret;
- this.redirectDomain = redirectDomain;
- googleAuthorizationCodeFlow = new GoogleAuthorizationCodeFlow.Builder(
- new NetHttpTransport(),
- GsonFactory.getDefaultInstance(),
- clientId,
- secret,
- Arrays.asList(
- "https://www.googleapis.com/auth/userinfo.profile",
- "https://www.googleapis.com/auth/userinfo.email"
- )
- ).build();
- }
-
- public String getName() {
- return "google";
- }
-
- public GoogleOAuth2Service setScopes(String[] scopes) {
- googleAuthorizationCodeFlow.getScopes().clear();
- for (String scope : scopes) {
- googleAuthorizationCodeFlow.getScopes().add(scope);
- }
- return this;
- }
-
- public OAuth2Callback callback(Exchange exchange, OAuth2Module oAuth2Module) {
- GoogleTokenResponse code = null;
- try {
- code = googleAuthorizationCodeFlow.newTokenRequest(exchange.rawRequest().getParameter("code"))
- .setRedirectUri(redirectDomain+oAuth2Module.getPathPrefix()+getName()+"/callback")
- .execute();
- } catch (IOException e) {
- e.printStackTrace();
- throw new RuntimeException();
- }
- Profile profile = getProfile(code.getAccessToken());
- if (profile != null)
- return new OAuth2Callback(code.getAccessToken(), profile, new HTTPClient("https://www.googleapis.com/oauth2/v1").bearer(code.getAccessToken())).setRefreshToken(code.getRefreshToken());
-
- return null;
- }
-
-
- public Object redirect(Exchange exchange, OAuth2Module oAuth2Module) {
- exchange.redirect(
- googleAuthorizationCodeFlow
- .newAuthorizationUrl()
- .setAccessType("offline")
- .setRedirectUri(redirectDomain+oAuth2Module.getPathPrefix()+getName()+"/callback")
- .build()
- );
- return "";
- }
-
- @Override
- public Profile getProfile(String accessToken) {
- Credential credential = new GoogleCredential.Builder()
- .setTransport(new NetHttpTransport())
- .setJsonFactory(GsonFactory.getDefaultInstance())
- .setClientSecrets(clientId, secret)
- .build().setAccessToken(accessToken);
-
- Oauth2 oauth2 = new Oauth2.Builder(
- new NetHttpTransport(),
- GsonFactory.getDefaultInstance(),
- credential
- ).setApplicationName(clientId).build();
- try {
- Profile profile = new Profile();
- Userinfo userinfo = oauth2.userinfo().get().execute();
- profile.id = userinfo.getId();
- profile.name = userinfo.getName();
- profile.avatar = userinfo.getPicture();
- profile.mail = userinfo.getEmail();
- userinfo.forEach((key, val)->{
- profile.set(key, val.toString());
- });
- return profile;
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- return null;
- }
-
- public GoogleAuthorizationCodeFlow getGoogleAuthorizationCodeFlow() {
- return googleAuthorizationCodeFlow;
- }
-
- public String refreshToken(String refreshToken){
- return googleAuthorizationCodeFlow.newTokenRequest(refreshToken).getCode();
- }
-}
diff --git a/src/main/java/org/javawebstack/passport/services/oauth2/InteraAppsOAuth2Service.java b/src/main/java/org/javawebstack/passport/services/oauth2/InteraAppsOAuth2Service.java
deleted file mode 100644
index be00a6a..0000000
--- a/src/main/java/org/javawebstack/passport/services/oauth2/InteraAppsOAuth2Service.java
+++ /dev/null
@@ -1,107 +0,0 @@
-package org.javawebstack.passport.services.oauth2;
-
-import org.javawebstack.abstractdata.AbstractElement;
-import org.javawebstack.abstractdata.AbstractObject;
-import org.javawebstack.httpclient.HTTPClient;
-import org.javawebstack.httpserver.Exchange;
-import org.javawebstack.passport.OAuth2Module;
-import org.javawebstack.passport.Profile;
-
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.util.Arrays;
-import java.util.List;
-
-public class InteraAppsOAuth2Service extends HTTPClient implements OAuth2Service {
- private final String clientId;
- private final String secret;
- private String[] scopes;
- private String redirectDomain;
-
- public InteraAppsOAuth2Service(String clientId, String secret, String redirectDomain){
- this.redirectDomain = redirectDomain;
- setBaseUrl("https://accounts.interaapps.de/api/v2");
- this.clientId = clientId;
- this.secret = secret;
- scopes = new String[]{"user:read"};
- }
-
- public InteraAppsOAuth2Service setScopes(String[] scopes) {
- this.scopes = scopes;
- return this;
- }
-
- public OAuth2Callback callback(Exchange exchange, OAuth2Module oAuth2Module){
- AbstractObject data = post("/authorization/oauth2/access_token")
- .jsonBodyElement(new AbstractObject()
- .set("client_id", clientId)
- .set("client_secret", secret)
- .set("code", exchange.rawRequest().getParameter("code"))
- )
- .data()
- .object();
-
- List scopes = Arrays.asList(this.scopes);
-
- for (AbstractElement scope : data.get("scope_list").array()) {
- if (!scopes.contains(scope.string()))
- return null;
- }
-
- if (data.get("success").bool()) {
-
- String accessToken = data.get("access_token").string();
-
-
- return new OAuth2Callback(accessToken, getProfile(accessToken), new HTTPClient("https://accounts.interaapps.de/").bearer(accessToken)).setRefreshToken(data.get("refresh_token").string());
- }
-
- return null;
- }
-
- public Object redirect(Exchange exchange, OAuth2Module oAuth2Module) {
- try {
- exchange.redirect("https://accounts.interaapps.de/auth/oauth2?client_id="+clientId+"&scope="+URLEncoder.encode(String.join(" ", scopes), "UTF-8")+"&redirect_uri="+URLEncoder.encode(redirectDomain+oAuth2Module.getPathPrefix()+getName()+"/callback", "UTF-8"));
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- return null;
- }
- return "";
- }
-
- public Profile getProfile(String accessToken) {
- AbstractObject userData = get("/user")
- .bearer(accessToken)
- .data().object();
- Profile profile = new Profile();
-
- if (userData.has("id"))
- profile.id = userData.get("id").number().toString();
- if (userData.has("name"))
- profile.name = userData.get("name").string();
- if (userData.has("mail"))
- profile.mail = userData.get("mail").string();
- if (userData.has("profile_picture"))
- profile.avatar = userData.get("profile_picture").string();
-
- userData.forEach(profile::set);
- return profile;
- }
-
-
- public String getName() {
- return "interaapps";
- }
-
- public String refreshToken(String refreshToken){
- return post("/authorization/oauth2/access_token")
- .jsonBodyElement(new AbstractObject()
- .set("client_id", clientId)
- .set("client_secret", secret)
- .set("code", refreshToken)
- )
- .data()
- .object().get("access_token").string();
- }
-
-}
diff --git a/src/main/java/org/javawebstack/passport/services/oauth2/OAuth2Callback.java b/src/main/java/org/javawebstack/passport/services/oauth2/OAuth2Callback.java
deleted file mode 100644
index b935986..0000000
--- a/src/main/java/org/javawebstack/passport/services/oauth2/OAuth2Callback.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package org.javawebstack.passport.services.oauth2;
-
-import org.javawebstack.httpclient.HTTPClient;
-import org.javawebstack.passport.Profile;
-
-public class OAuth2Callback {
- private String token;
- private Profile profile;
- private HTTPClient httpClient;
- private String refreshToken;
-
- public OAuth2Callback(String token, Profile profile, HTTPClient httpClient) {
- this.token = token;
- this.profile = profile;
- this.httpClient = httpClient;
- }
-
- public Profile getProfile() {
- return profile;
- }
-
- public String getToken() {
- return token;
- }
-
- public HTTPClient getHttpClient() {
- return httpClient;
- }
-
- public OAuth2Callback setRefreshToken(String refreshToken) {
- this.refreshToken = refreshToken;
- return this;
- }
-
- public String getRefreshToken() {
- return refreshToken;
- }
-
-}
diff --git a/src/main/java/org/javawebstack/passport/services/oauth2/OAuth2CallbackHandler.java b/src/main/java/org/javawebstack/passport/services/oauth2/OAuth2CallbackHandler.java
deleted file mode 100644
index b5b0aa8..0000000
--- a/src/main/java/org/javawebstack/passport/services/oauth2/OAuth2CallbackHandler.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package org.javawebstack.passport.services.oauth2;
-
-import org.javawebstack.httpclient.HTTPClient;
-import org.javawebstack.httpserver.Exchange;
-
-public interface OAuth2CallbackHandler {
- Object callback(String service, Exchange exchange, OAuth2Callback callback);
-}
diff --git a/src/main/java/org/javawebstack/passport/services/oauth2/OAuth2Service.java b/src/main/java/org/javawebstack/passport/services/oauth2/OAuth2Service.java
deleted file mode 100644
index c3b2d87..0000000
--- a/src/main/java/org/javawebstack/passport/services/oauth2/OAuth2Service.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.javawebstack.passport.services.oauth2;
-
-import org.javawebstack.httpserver.Exchange;
-import org.javawebstack.passport.AuthService;
-import org.javawebstack.passport.OAuth2Module;
-import org.javawebstack.passport.Profile;
-
-public interface OAuth2Service extends AuthService {
- OAuth2Callback callback(Exchange exchange, OAuth2Module oAuth2Module);
-
- Object redirect(Exchange exchange, OAuth2Module oAuth2Module);
-
- Profile getProfile(String accessToken);
-}
diff --git a/src/main/java/org/javawebstack/passport/services/oauth2/TwitchOAuth2Service.java b/src/main/java/org/javawebstack/passport/services/oauth2/TwitchOAuth2Service.java
deleted file mode 100644
index d39fd08..0000000
--- a/src/main/java/org/javawebstack/passport/services/oauth2/TwitchOAuth2Service.java
+++ /dev/null
@@ -1,112 +0,0 @@
-package org.javawebstack.passport.services.oauth2;
-
-import org.javawebstack.abstractdata.AbstractObject;
-import org.javawebstack.abstractdata.util.QueryString;
-import org.javawebstack.httpclient.HTTPClient;
-import org.javawebstack.httpserver.Exchange;
-import org.javawebstack.httpserver.helper.MimeType;
-import org.javawebstack.passport.OAuth2Module;
-import org.javawebstack.passport.Profile;
-
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-
-public class TwitchOAuth2Service extends HTTPClient implements OAuth2Service {
- private final String clientId;
- private final String clientSecret;
- private String[] scopes = new String[]{"user:read:email"}; // reference: https://dev.twitch.tv/docs/authentication/#scopes
- private final String redirectDomain;
- private boolean forceVerify = false;
- private String state;
- private OAuth2Module oAuth2Module;
-
- public TwitchOAuth2Service(String clientId, String clientSecret, String redirectDomain, OAuth2Module oAuth2Module) {
- this.oAuth2Module = oAuth2Module;
- setBaseUrl("https://api.twitch.tv/helix");
- this.clientId = clientId;
- this.redirectDomain = redirectDomain;
- this.clientSecret = clientSecret;
- }
-
- public TwitchOAuth2Service setState(String state) {
- this.state = state;
- return this;
- }
-
- public String getState() {
- return state;
- }
-
- public TwitchOAuth2Service setScopes(String[] scopes) {
- this.scopes = scopes;
- return this;
- }
-
- public TwitchOAuth2Service setForceVerify(boolean forceVerify) {
- this.forceVerify = forceVerify;
- return this;
- }
-
- public boolean isForceVerify() {
- return forceVerify;
- }
-
- public String getName() {
- return "twitch";
- }
-
- public OAuth2Callback callback(Exchange exchange, OAuth2Module oAuth2Module) {
- AbstractObject abstractObject = new HTTPClient("https://id.twitch.tv").post("/oauth2/token")
- .formBodyString(new QueryString()
- .set("client_id", clientId)
- .set("client_secret", clientSecret)
- .set("code", exchange.rawRequest().getParameter("code"))
- .set("grant_type", "authorization_code")
- .set("redirect_uri", createRedirectUrl(oAuth2Module.getPathPrefix()))
- .toString())
- .header("Accept", MimeType.JSON.getMimeTypes().get(0))
- .data().object();
-
- if (abstractObject.has("scope")) {
- String accessToken = abstractObject.get("access_token").string();
- return new OAuth2Callback(accessToken, getProfile(accessToken), new HTTPClient("https://api.twitch.tv/helix").bearer(accessToken));
- }
-
- return null;
- }
-
- public Object redirect(Exchange exchange, OAuth2Module oAuth2Module) {
- try {
- exchange.redirect("https://id.twitch.tv/oauth2/authorize?client_id="+clientId+"&response_type=code&scope="+ URLEncoder.encode(String.join(" ", scopes), "UTF-8")+"&redirect_uri="+URLEncoder.encode(createRedirectUrl(oAuth2Module.getPathPrefix()), "UTF-8")+"&force_verify="+forceVerify+"&state="+state);
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- return null;
- }
- return "";
- }
-
- private String createRedirectUrl(String redirectPathPrefix){
- return redirectDomain +redirectPathPrefix+getName()+"/callback";
- }
-
- public Profile getProfile(String accessToken) {
- Profile profile = new Profile();
-
- AbstractObject userData = get("/users")
- .bearer(accessToken)
- .header("Client-Id", clientId)
- .data().object().get("data").array().get(0).object();
-
- if (userData.has("id"))
- profile.id = userData.get("id").string();
- if (userData.has("login"))
- profile.name = userData.get("login").string();
- if (userData.has("profile_image_url"))
- profile.avatar = userData.get("profile_image_url").string();
- if (userData.has("email"))
- profile.mail = userData.get("email").string();
- userData.forEach(profile::set);
-
- return profile;
- }
-}
diff --git a/src/main/java/org/javawebstack/passport/strategies/Strategy.java b/src/main/java/org/javawebstack/passport/strategies/Strategy.java
new file mode 100644
index 0000000..c6ecf03
--- /dev/null
+++ b/src/main/java/org/javawebstack/passport/strategies/Strategy.java
@@ -0,0 +1,23 @@
+package org.javawebstack.passport.strategies;
+
+import org.javawebstack.httpserver.HTTPServer;
+import org.javawebstack.passport.Passport;
+
+public abstract class Strategy {
+ protected String name;
+ protected Passport passport;
+
+ public abstract void createRoutes(String prefixUrl, HTTPServer httpServer);
+
+ public String getName() {
+ return name;
+ }
+
+ public Passport getPassport() {
+ return passport;
+ }
+
+ public void setPassport(Passport passport) {
+ this.passport = passport;
+ }
+}
diff --git a/src/main/java/org/javawebstack/passport/strategies/oauth2/OAuth2Callback.java b/src/main/java/org/javawebstack/passport/strategies/oauth2/OAuth2Callback.java
new file mode 100644
index 0000000..438f660
--- /dev/null
+++ b/src/main/java/org/javawebstack/passport/strategies/oauth2/OAuth2Callback.java
@@ -0,0 +1,25 @@
+package org.javawebstack.passport.strategies.oauth2;
+
+import org.javawebstack.httpclient.HTTPClient;
+
+public abstract class OAuth2Callback {
+ protected String accessToken;
+ protected String refreshToken;
+
+ public OAuth2Callback(String accessToken, String refreshToken) {
+ this.accessToken = accessToken;
+ this.refreshToken = refreshToken;
+ }
+
+ public abstract HTTPClient createApiClient();
+
+ public abstract OAuth2Profile getProfile();
+
+ public String getAccessToken() {
+ return accessToken;
+ }
+
+ public String getRefreshToken() {
+ return refreshToken;
+ }
+}
diff --git a/src/main/java/org/javawebstack/passport/Profile.java b/src/main/java/org/javawebstack/passport/strategies/oauth2/OAuth2Profile.java
similarity index 78%
rename from src/main/java/org/javawebstack/passport/Profile.java
rename to src/main/java/org/javawebstack/passport/strategies/oauth2/OAuth2Profile.java
index ac8ca68..5a480d5 100644
--- a/src/main/java/org/javawebstack/passport/Profile.java
+++ b/src/main/java/org/javawebstack/passport/strategies/oauth2/OAuth2Profile.java
@@ -1,8 +1,8 @@
-package org.javawebstack.passport;
+package org.javawebstack.passport.strategies.oauth2;
import org.javawebstack.abstractdata.AbstractObject;
-public class Profile extends AbstractObject {
+public class OAuth2Profile extends AbstractObject {
public String id;
public String name;
public String mail;
diff --git a/src/main/java/org/javawebstack/passport/strategies/oauth2/OAuth2Provider.java b/src/main/java/org/javawebstack/passport/strategies/oauth2/OAuth2Provider.java
new file mode 100644
index 0000000..379ccff
--- /dev/null
+++ b/src/main/java/org/javawebstack/passport/strategies/oauth2/OAuth2Provider.java
@@ -0,0 +1,9 @@
+package org.javawebstack.passport.strategies.oauth2;
+
+import org.javawebstack.abstractdata.AbstractObject;
+
+public abstract class OAuth2Provider {
+ public abstract OAuth2Callback callback(AbstractObject queryParameters, String callbackUrl);
+
+ public abstract String redirect(String callbackUrl);
+}
diff --git a/src/main/java/org/javawebstack/passport/strategies/oauth2/OAuth2Strategy.java b/src/main/java/org/javawebstack/passport/strategies/oauth2/OAuth2Strategy.java
new file mode 100644
index 0000000..4726ed2
--- /dev/null
+++ b/src/main/java/org/javawebstack/passport/strategies/oauth2/OAuth2Strategy.java
@@ -0,0 +1,51 @@
+package org.javawebstack.passport.strategies.oauth2;
+
+import org.javawebstack.httpserver.Exchange;
+import org.javawebstack.httpserver.HTTPServer;
+import org.javawebstack.passport.strategies.Strategy;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class OAuth2Strategy extends Strategy {
+ private Map providers = new HashMap<>();
+ private HttpCallbackHandler httpCallbackHandler;
+ private String host;
+
+
+ public OAuth2Strategy(String host){
+
+ this.host = host;
+ }
+
+ public OAuth2Strategy use(String name, OAuth2Provider oAuth2Provider){
+ providers.put(name, oAuth2Provider);
+ return this;
+ }
+
+ public void createRoutes(String prefixUrl, HTTPServer httpServer) {
+ providers.forEach((name, oauth2) -> {
+ final String callbackUrl = prefixUrl+"/"+name+"/callback";
+ httpServer.get(prefixUrl+"/"+name, e -> {
+ e.redirect(oauth2.redirect(host+callbackUrl));
+ return "";
+ });
+
+ httpServer.get(callbackUrl, e -> {
+ return httpCallbackHandler.handle(e, oauth2.callback(e.getQueryParameters(), host+callbackUrl));
+ });
+ });
+ }
+
+ public Map getProviders() {
+ return providers;
+ }
+
+ public void setHttpCallbackHandler(HttpCallbackHandler httpCallbackHandler) {
+ this.httpCallbackHandler = httpCallbackHandler;
+ }
+
+ public interface HttpCallbackHandler {
+ Object handle(Exchange exchange, OAuth2Callback callback);
+ }
+}
diff --git a/src/main/java/org/javawebstack/passport/strategies/oauth2/providers/DiscordOAuth2Provider.java b/src/main/java/org/javawebstack/passport/strategies/oauth2/providers/DiscordOAuth2Provider.java
new file mode 100644
index 0000000..a7bed3e
--- /dev/null
+++ b/src/main/java/org/javawebstack/passport/strategies/oauth2/providers/DiscordOAuth2Provider.java
@@ -0,0 +1,90 @@
+package org.javawebstack.passport.strategies.oauth2.providers;
+
+import org.javawebstack.abstractdata.AbstractObject;
+import org.javawebstack.abstractdata.util.QueryString;
+import org.javawebstack.httpclient.HTTPClient;
+import org.javawebstack.passport.strategies.oauth2.OAuth2Callback;
+import org.javawebstack.passport.strategies.oauth2.OAuth2Profile;
+import org.javawebstack.passport.strategies.oauth2.OAuth2Provider;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+public class DiscordOAuth2Provider extends OAuth2Provider {
+ private String clientId;
+ private String secret;
+ private String[] scopes = {"email","identify"};
+ private HTTPClient discordClient;
+
+ public DiscordOAuth2Provider(String clientId, String secret){
+ this.clientId = clientId;
+ this.secret = secret;
+ discordClient = new HTTPClient("https://discord.com");
+ }
+
+ public DiscordOAuth2Provider setScopes(String... scopes) {
+ this.scopes = scopes;
+ return this;
+ }
+
+ public OAuth2Callback callback(AbstractObject queryParameters, String callbackUrl) {
+ AbstractObject abstractObject = discordClient.post("/api/oauth2/token")
+ .header("User-Agent", "JWSPassportClient/1")
+ .formBodyString(new QueryString()
+ .set("client_id", clientId)
+ .set("client_secret", secret)
+ .set("code", queryParameters.string("code"))
+ .set("grant_type", "authorization_code")
+ .set("redirect_uri", callbackUrl)
+ .set("scope", String.join(" ", scopes))
+ .toString()
+ )
+ .data().object();
+ return new OAuth2Callback(abstractObject.string("access_token"), abstractObject.string("refresh_token"));
+ }
+
+ public String redirect(String callbackUrl) {
+ try {
+ return "https://discord.com/api/oauth2/authorize?response_type=code&client_id="+clientId+"&prompt=consent&scope="+ URLEncoder.encode(String.join(" ", scopes), "UTF-8")+"&redirect_uri="+URLEncoder.encode(callbackUrl, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+ public static class OAuth2Callback extends org.javawebstack.passport.strategies.oauth2.OAuth2Callback {
+ public OAuth2Callback(String accessToken, String refreshToken) {
+ super(accessToken, refreshToken);
+ }
+
+ public HTTPClient createApiClient() {
+ return new HTTPClient("https://discord.com/").bearer(accessToken);
+ }
+
+ public OAuth2Profile getProfile() {
+ OAuth2Profile profile = new OAuth2Profile();
+
+ AbstractObject data = createApiClient().get("/api/users/@me")
+ .bearer(accessToken)
+ .header("User-Agent", "JWSPassportClient/1")
+ .data().object();
+
+ if (data.has("id")) {
+ profile.id = data.get("id").string();
+
+ if (data.has("avatar"))
+ profile.avatar = "https://cdn.discordapp.com/avatars/"+profile.id+"/"+data.get("avatar").string()+".png";
+ }
+
+ if (data.has("username"))
+ profile.name = data.get("username").string();
+
+ if (data.has("email"))
+ profile.mail = data.get("email").string();
+
+ data.forEach(profile::set);
+
+ return profile;
+ }
+ }
+}
diff --git a/src/main/java/org/javawebstack/passport/strategies/oauth2/providers/GitHubOAuth2Provider.java b/src/main/java/org/javawebstack/passport/strategies/oauth2/providers/GitHubOAuth2Provider.java
new file mode 100644
index 0000000..5be7639
--- /dev/null
+++ b/src/main/java/org/javawebstack/passport/strategies/oauth2/providers/GitHubOAuth2Provider.java
@@ -0,0 +1,94 @@
+package org.javawebstack.passport.strategies.oauth2.providers;
+
+import org.javawebstack.abstractdata.AbstractElement;
+import org.javawebstack.abstractdata.AbstractObject;
+import org.javawebstack.abstractdata.util.QueryString;
+import org.javawebstack.httpclient.HTTPClient;
+import org.javawebstack.httpserver.helper.MimeType;
+import org.javawebstack.passport.strategies.oauth2.OAuth2Profile;
+import org.javawebstack.passport.strategies.oauth2.OAuth2Provider;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Arrays;
+import java.util.List;
+
+public class GitHubOAuth2Provider extends OAuth2Provider {
+ private String clientId;
+ private String secret;
+ private String[] scopes = {"read:user","user:email"};
+ public GitHubOAuth2Provider(String clientId, String secret){
+ this.clientId = clientId;
+ this.secret = secret;
+ }
+
+ public GitHubOAuth2Provider setScopes(String... scopes) {
+ this.scopes = scopes;
+ return this;
+ }
+
+ public OAuth2Callback callback(AbstractObject queryParameters, String callbackUrl) {
+ AbstractObject abstractObject = new HTTPClient("https://github.com").post("/login/oauth/access_token")
+ .formBodyString(new QueryString()
+ .set("client_id", clientId)
+ .set("client_secret", secret)
+ .set("code", queryParameters.string("code"))
+ .toString())
+ .header("Accept", MimeType.JSON.getMimeTypes().get(0))
+ .data().object();
+
+
+ if (abstractObject.has("scope")/* && abstractObject.get("scope").string().equals("read:user,user:email")*/) {
+ System.out.println(abstractObject.toJsonString());
+ return new OAuth2Callback(abstractObject.string("access_token"), abstractObject.has("refresh_token") ? abstractObject.string("refresh_token") : null);
+ }
+
+ return null;
+ }
+
+ public String redirect(String callbackUrl) {
+ try {
+ return "https://github.com/login/oauth/authorize?client_id="+clientId+"&scope="+ URLEncoder.encode(String.join(" ", scopes), "UTF-8")+"&redirect_uri="+URLEncoder.encode(callbackUrl, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+ public static class OAuth2Callback extends org.javawebstack.passport.strategies.oauth2.OAuth2Callback {
+ public OAuth2Callback(String accessToken, String refreshToken) {
+ super(accessToken, refreshToken);
+ }
+
+ public HTTPClient createApiClient() {
+ return new HTTPClient("https://api.github.com").authorization("token", accessToken);
+ }
+
+ public OAuth2Profile getProfile() {
+ HTTPClient apiClient = createApiClient();
+ OAuth2Profile profile = new OAuth2Profile();
+
+ AbstractObject userData = apiClient.get("/user")
+ .header("Authorization", "token "+accessToken)
+ .data().object();
+
+ if (userData.has("id"))
+ profile.id = userData.get("id").number().toString();
+ if (userData.has("name"))
+ profile.name = userData.get("name").string();
+ if (userData.has("avatar_url"))
+ profile.avatar = userData.get("avatar_url").string();
+
+ userData.forEach(profile::set);
+
+ apiClient.get("/user/emails")
+ .authorization("token", accessToken)
+ .data().array().forEach(abstractElement -> {
+ if (profile.mail == null) {
+ profile.mail = abstractElement.object().get("email").string();
+ }
+ });
+ return profile;
+ }
+ }
+}
diff --git a/src/main/java/org/javawebstack/passport/strategies/oauth2/providers/GoogleOAuth2Provider.java b/src/main/java/org/javawebstack/passport/strategies/oauth2/providers/GoogleOAuth2Provider.java
new file mode 100644
index 0000000..6ae9e5a
--- /dev/null
+++ b/src/main/java/org/javawebstack/passport/strategies/oauth2/providers/GoogleOAuth2Provider.java
@@ -0,0 +1,113 @@
+package org.javawebstack.passport.strategies.oauth2.providers;
+
+import com.google.api.client.auth.oauth2.Credential;
+import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
+import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
+import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;
+import com.google.api.client.http.javanet.NetHttpTransport;
+import com.google.api.client.json.gson.GsonFactory;
+import com.google.api.services.oauth2.Oauth2;
+import com.google.api.services.oauth2.model.Userinfo;
+import org.javawebstack.abstractdata.AbstractObject;
+import org.javawebstack.httpclient.HTTPClient;
+import org.javawebstack.passport.strategies.oauth2.OAuth2Callback;
+import org.javawebstack.passport.strategies.oauth2.OAuth2Profile;
+import org.javawebstack.passport.strategies.oauth2.OAuth2Provider;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+public class GoogleOAuth2Provider extends OAuth2Provider {
+ private String clientId;
+ private String secret;
+ private GoogleAuthorizationCodeFlow googleAuthorizationCodeFlow;
+
+ public GoogleOAuth2Provider(String clientId, String secret){
+ this.clientId = clientId;
+ this.secret = secret;
+
+ googleAuthorizationCodeFlow = new GoogleAuthorizationCodeFlow.Builder(
+ new NetHttpTransport(),
+ GsonFactory.getDefaultInstance(),
+ clientId,
+ secret,
+ Arrays.asList(
+ "https://www.googleapis.com/auth/userinfo.profile",
+ "https://www.googleapis.com/auth/userinfo.email"
+ )
+ ).build();
+ }
+
+ public GoogleOAuth2Provider setScopes(String[] scopes) {
+ googleAuthorizationCodeFlow.getScopes().clear();
+ for (String scope : scopes) {
+ googleAuthorizationCodeFlow.getScopes().add(scope);
+ }
+ return this;
+ }
+
+ public OAuth2Callback callback(AbstractObject queryParameters, String callbackUrl) {
+ try {
+ GoogleTokenResponse code =googleAuthorizationCodeFlow.newTokenRequest(queryParameters.string("code"))
+ .setRedirectUri(callbackUrl)
+ .execute();
+ return new OAuth2Callback(code.getAccessToken(), code.getRefreshToken(), clientId, secret);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public String redirect(String callbackUrl) {
+ return googleAuthorizationCodeFlow
+ .newAuthorizationUrl()
+ .setAccessType("offline")
+ .setRedirectUri(callbackUrl)
+ .build();
+ }
+
+ public static class OAuth2Callback extends org.javawebstack.passport.strategies.oauth2.OAuth2Callback {
+ private String clientId;
+ private String secret;
+
+ public OAuth2Callback(String accessToken, String refreshToken, String clientId, String secret) {
+ super(accessToken, refreshToken);
+ this.clientId = clientId;
+ this.secret = secret;
+ }
+
+ public HTTPClient createApiClient() {
+ return new HTTPClient("https://www.googleapis.com/oauth2/v1").bearer(accessToken);
+ }
+
+ public OAuth2Profile getProfile() {
+ Credential credential = new GoogleCredential.Builder()
+ .setTransport(new NetHttpTransport())
+ .setJsonFactory(GsonFactory.getDefaultInstance())
+ .setClientSecrets(clientId, secret)
+ .build().setAccessToken(accessToken);
+
+ Oauth2 oauth2 = new Oauth2.Builder(
+ new NetHttpTransport(),
+ GsonFactory.getDefaultInstance(),
+ credential
+ ).setApplicationName(clientId).build();
+ try {
+ OAuth2Profile profile = new OAuth2Profile();
+ Userinfo userinfo = oauth2.userinfo().get().execute();
+ profile.id = userinfo.getId();
+ profile.name = userinfo.getName();
+ profile.avatar = userinfo.getPicture();
+ profile.mail = userinfo.getEmail();
+ userinfo.forEach((key, val)->{
+ profile.set(key, val.toString());
+ });
+ return profile;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/javawebstack/passport/strategies/oauth2/providers/InteraAppsOAuth2Provider.java b/src/main/java/org/javawebstack/passport/strategies/oauth2/providers/InteraAppsOAuth2Provider.java
new file mode 100644
index 0000000..0544afc
--- /dev/null
+++ b/src/main/java/org/javawebstack/passport/strategies/oauth2/providers/InteraAppsOAuth2Provider.java
@@ -0,0 +1,95 @@
+package org.javawebstack.passport.strategies.oauth2.providers;
+
+import org.javawebstack.abstractdata.AbstractElement;
+import org.javawebstack.abstractdata.AbstractObject;
+import org.javawebstack.httpclient.HTTPClient;
+import org.javawebstack.httpclient.HTTPRequest;
+import org.javawebstack.passport.strategies.oauth2.OAuth2Callback;
+import org.javawebstack.passport.strategies.oauth2.OAuth2Profile;
+import org.javawebstack.passport.strategies.oauth2.OAuth2Provider;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Arrays;
+import java.util.List;
+
+public class InteraAppsOAuth2Provider extends OAuth2Provider {
+ private String clientId;
+ private String secret;
+ private String[] scopes = {"user:read"};
+ private HTTPClient interaAppsAccountsClient;
+
+ public InteraAppsOAuth2Provider(String clientId, String secret){
+ this.clientId = clientId;
+ this.secret = secret;
+ interaAppsAccountsClient = new HTTPClient("https://accounts.interaapps.de/api/v2");
+ }
+
+ public InteraAppsOAuth2Provider setScopes(String... scopes) {
+ this.scopes = scopes;
+ return this;
+ }
+
+ public OAuth2Callback callback(AbstractObject queryParameters, String callbackUrl) {
+ AbstractObject data = interaAppsAccountsClient.post("/authorization/oauth2/access_token")
+ .jsonBodyElement(new AbstractObject()
+ .set("client_id", clientId)
+ .set("client_secret", secret)
+ .set("code", queryParameters.string("code"))
+ )
+ .data()
+ .object();
+
+ List scopes = Arrays.asList(this.scopes);
+
+ if (!data.bool("success"))
+ return null;
+
+ for (AbstractElement scope : data.get("scope_list").array()) {
+ if (!scopes.contains(scope.string()))
+ return null;
+ }
+
+ if (data.get("success").bool()) {
+ return new OAuth2Callback(data.string("access_token"), data.string("refresh_token"));
+ }
+
+ return null;
+ }
+
+ public String redirect(String callbackUrl) {
+ try {
+ return "https://accounts.interaapps.de/auth/oauth2?client_id="+clientId+"&scope="+ URLEncoder.encode(String.join(" ", scopes), "UTF-8")+"&redirect_uri="+URLEncoder.encode(callbackUrl, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+ public static class OAuth2Callback extends org.javawebstack.passport.strategies.oauth2.OAuth2Callback {
+ public OAuth2Callback(String accessToken, String refreshToken) {
+ super(accessToken, refreshToken);
+ }
+
+ public HTTPClient createApiClient() {
+ return new HTTPClient("https://accounts.interaapps.de/api/v2").bearer(accessToken);
+ }
+
+ public OAuth2Profile getProfile() {
+ AbstractObject userData = createApiClient().get("/user").data().object();
+ OAuth2Profile profile = new OAuth2Profile();
+
+ if (userData.has("id"))
+ profile.id = userData.get("id").number().toString();
+ if (userData.has("name"))
+ profile.name = userData.get("name").string();
+ if (userData.has("mail"))
+ profile.mail = userData.get("mail").string();
+ if (userData.has("profile_picture"))
+ profile.avatar = userData.get("profile_picture").string();
+
+ userData.forEach(profile::set);
+ return profile;
+ }
+ }
+}
diff --git a/src/main/java/org/javawebstack/passport/strategies/oauth2/providers/TwitchOAuth2Provider.java b/src/main/java/org/javawebstack/passport/strategies/oauth2/providers/TwitchOAuth2Provider.java
new file mode 100644
index 0000000..5cf327a
--- /dev/null
+++ b/src/main/java/org/javawebstack/passport/strategies/oauth2/providers/TwitchOAuth2Provider.java
@@ -0,0 +1,109 @@
+package org.javawebstack.passport.strategies.oauth2.providers;
+
+import org.javawebstack.abstractdata.AbstractObject;
+import org.javawebstack.abstractdata.util.QueryString;
+import org.javawebstack.httpclient.HTTPClient;
+import org.javawebstack.httpserver.helper.MimeType;
+import org.javawebstack.passport.strategies.oauth2.OAuth2Profile;
+import org.javawebstack.passport.strategies.oauth2.OAuth2Provider;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+public class TwitchOAuth2Provider extends OAuth2Provider {
+ private String clientId;
+ private String secret;
+ private String[] scopes = {"user:read:email"}; // reference: https://dev.twitch.tv/docs/authentication/#scopes
+
+ private boolean forceVerify = false;
+ private String state;
+
+ public TwitchOAuth2Provider(String clientId, String secret){
+ this.clientId = clientId;
+ this.secret = secret;
+ }
+
+ public TwitchOAuth2Provider setScopes(String... scopes) {
+ this.scopes = scopes;
+ return this;
+ }
+
+ public OAuth2Callback callback(AbstractObject queryParameters, String callbackUrl) {
+ AbstractObject abstractObject = new HTTPClient("https://id.twitch.tv").post("/oauth2/token")
+ .formBodyString(new QueryString()
+ .set("client_id", clientId)
+ .set("client_secret", secret)
+ .set("code", queryParameters.string("code"))
+ .set("grant_type", "authorization_code")
+ .set("redirect_uri", callbackUrl)
+ .toString())
+ .header("Accept", MimeType.JSON.getMimeTypes().get(0))
+ .data().object();
+
+ if (abstractObject.has("scope")) {
+ return new OAuth2Callback(abstractObject.string("access_token"), abstractObject.string("refresh_token"), clientId);
+ }
+
+ return null;
+ }
+
+ public TwitchOAuth2Provider setState(String state) {
+ this.state = state;
+ return this;
+ }
+
+ public String getState() {
+ return state;
+ }
+
+ public TwitchOAuth2Provider setForceVerify(boolean forceVerify) {
+ this.forceVerify = forceVerify;
+ return this;
+ }
+
+ public boolean isForceVerify() {
+ return forceVerify;
+ }
+
+ public String redirect(String callbackUrl) {
+ try {
+ return "https://id.twitch.tv/oauth2/authorize?client_id="+clientId+"&response_type=code&scope="+ URLEncoder.encode(String.join(" ", scopes), "UTF-8")+"&redirect_uri="+URLEncoder.encode(callbackUrl, "UTF-8")+"&force_verify="+forceVerify+"&state="+state;
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+ public static class OAuth2Callback extends org.javawebstack.passport.strategies.oauth2.OAuth2Callback {
+ private String clientId;
+
+ public OAuth2Callback(String accessToken, String refreshToken, String clientId) {
+ super(accessToken, refreshToken);
+ this.clientId = clientId;
+ }
+
+ public HTTPClient createApiClient() {
+ return new HTTPClient("https://api.twitch.tv/helix").bearer(accessToken);
+ }
+
+ public OAuth2Profile getProfile() {
+ OAuth2Profile profile = new OAuth2Profile();
+
+ AbstractObject userData = createApiClient().get("/users")
+ .header("Client-Id", clientId)
+ .data().object().get("data").array().get(0).object();
+
+ if (userData.has("id"))
+ profile.id = userData.get("id").string();
+ if (userData.has("login"))
+ profile.name = userData.get("login").string();
+ if (userData.has("profile_image_url"))
+ profile.avatar = userData.get("profile_image_url").string();
+ if (userData.has("email"))
+ profile.mail = userData.get("email").string();
+ userData.forEach(profile::set);
+
+ return profile;
+ }
+ }
+}