mirror of
https://github.com/OpenLiberty/liberty-bikes.git
synced 2025-03-07 11:26:52 +08:00
Implement GitHub SSO option
This commit is contained in:
parent
b2075728ef
commit
2320d5d4ab
@ -10,6 +10,8 @@ liberty {
|
||||
dropins = [war]
|
||||
bootstrapProperties = ['httpPort': httpPort, 'httpsPort': httpsPort]
|
||||
configDirectory = file('src/main/liberty/config')
|
||||
jvmOptions = ['-Dorg.libertybikes.auth.service.github.GitHubOAuthAPI/mp-rest/url=https://github.com',
|
||||
'-Dorg.libertybikes.auth.service.github.GitHubUserAPI/mp-rest/url=https://api.github.com']
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
|
@ -0,0 +1,49 @@
|
||||
package org.libertybikes.auth.service.github;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.libertybikes.auth.service.AuthApp;
|
||||
|
||||
@Path("/GitHubAuth")
|
||||
@ApplicationScoped
|
||||
public class GitHubAuth {
|
||||
|
||||
private final static String GITHUB_AUTH_URL = "https://github.com/login/oauth/authorize";
|
||||
|
||||
@Inject
|
||||
@ConfigProperty(name = "github_key")
|
||||
String key;
|
||||
|
||||
@Inject
|
||||
@ConfigProperty(name = "auth_url", defaultValue = AuthApp.HTTPS_AUTH_SERVICE)
|
||||
String authUrl;
|
||||
|
||||
@GET
|
||||
public Response getAuthURL(@Context HttpServletRequest request) {
|
||||
try {
|
||||
String randomCode = UUID.randomUUID().toString();
|
||||
request.getSession().setAttribute("github", randomCode);
|
||||
|
||||
// GitHub will tell the users browser to go to this address once they are done authing.
|
||||
String callbackURL = authUrl + "/GitHubCallback";
|
||||
|
||||
// send the user to GitHub to be authenticated
|
||||
String redirectURL = GITHUB_AUTH_URL + "?client_id=" + key + "&redirect_url=" + callbackURL + "&scope=user:email&state=" + randomCode;
|
||||
return Response.temporaryRedirect(new URI(redirectURL)).build();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return Response.status(500).build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
package org.libertybikes.auth.service.github;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.Validator;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.libertybikes.auth.service.AuthApp;
|
||||
import org.libertybikes.auth.service.JwtAuth;
|
||||
import org.libertybikes.auth.service.github.GitHubOAuthAPI.GithubTokenResponse;
|
||||
import org.libertybikes.auth.service.github.GitHubUserAPI.EmailData;
|
||||
|
||||
@Path("/GitHubCallback")
|
||||
@ApplicationScoped
|
||||
public class GitHubCallback extends JwtAuth {
|
||||
|
||||
@Inject
|
||||
GitHubOAuthAPI githubOAuthAPI;
|
||||
|
||||
@Inject
|
||||
GitHubUserAPI githubUserAPI;
|
||||
|
||||
@Inject
|
||||
@ConfigProperty(name = "github_key")
|
||||
String key;
|
||||
|
||||
@Inject
|
||||
@ConfigProperty(name = "github_secret")
|
||||
String secret;
|
||||
|
||||
@Inject
|
||||
@ConfigProperty(name = "frontend_url", defaultValue = AuthApp.FRONTEND_URL)
|
||||
String frontendUrl;
|
||||
|
||||
@Inject
|
||||
@ConfigProperty(name = "auth_url", defaultValue = AuthApp.HTTPS_AUTH_SERVICE)
|
||||
String authUrl;
|
||||
|
||||
@Inject
|
||||
Validator validator;
|
||||
|
||||
@GET
|
||||
@Path("/test")
|
||||
@Valid
|
||||
public GithubTokenResponse testToken() {
|
||||
for (Method m : getClass().getMethods()) {
|
||||
System.out.println("@AGG found method: " + m.getName());
|
||||
for (Annotation a : m.getAnnotations())
|
||||
System.out.println(" anno=" + a);
|
||||
}
|
||||
GithubTokenResponse badToken = new GithubTokenResponse();
|
||||
badToken.access_token = "bogus";
|
||||
return badToken;
|
||||
}
|
||||
|
||||
// TODO: need to manually validate return types because the @Valid annotation on our MP Rest Client interface
|
||||
// is getting lost when the instance is injected into this class.
|
||||
private void validate(Object obj) {
|
||||
Objects.requireNonNull(obj);
|
||||
Set<ConstraintViolation<Object>> issues = validator.validate(obj);
|
||||
if (!issues.isEmpty()) {
|
||||
throw new ConstraintViolationException(issues);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
public Response getAuthURL(@Context HttpServletRequest request) throws IOException, URISyntaxException {
|
||||
try {
|
||||
String githubCode = request.getParameter("code");
|
||||
String randomCode = (String) request.getSession().getAttribute("github");
|
||||
|
||||
String thisURL = authUrl + "/GitHubCallback";
|
||||
|
||||
// First send the user through GitHub OAuth to get permission to read their email address
|
||||
GithubTokenResponse response = githubOAuthAPI.accessToken(key, secret, githubCode, thisURL, randomCode);
|
||||
// for (Method m : githubOAuthAPI.getClass().getMethods()) {
|
||||
// System.out.println("@AGG found method: " + m.getName());
|
||||
// for (Annotation a : m.getAnnotations())
|
||||
// System.out.println(" anno=" + a);
|
||||
// }
|
||||
validate(response);
|
||||
System.out.println("GitHub access token: " + response.access_token);
|
||||
|
||||
// Once we have the access token, use it to read their email
|
||||
EmailData[] emails = githubUserAPI.getEmail(response.access_token);
|
||||
for (EmailData email : emails)
|
||||
validate(email);
|
||||
String primaryEmail = null;
|
||||
for (EmailData data : emails)
|
||||
if (data.primary) {
|
||||
primaryEmail = data.email;
|
||||
break;
|
||||
} else {
|
||||
primaryEmail = data.email;
|
||||
}
|
||||
System.out.println("Got primary email of: " + primaryEmail);
|
||||
|
||||
Map<String, String> claims = new HashMap<String, String>();
|
||||
claims.put("valid", "true");
|
||||
claims.put("id", "GITHUB:" + primaryEmail);
|
||||
claims.put("upn", primaryEmail);
|
||||
claims.put("email", primaryEmail);
|
||||
return Response.temporaryRedirect(new URI(frontendUrl + "/" + createJwt(claims))).build();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return fail();
|
||||
}
|
||||
}
|
||||
|
||||
private Response fail() throws URISyntaxException {
|
||||
return Response.temporaryRedirect(new URI(frontendUrl)).build();
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package org.libertybikes.auth.service.github;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
@RegisterRestClient
|
||||
@Path("/login/oauth")
|
||||
public interface GitHubOAuthAPI {
|
||||
|
||||
public static class GithubTokenResponse {
|
||||
@NotNull
|
||||
public String access_token;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/access_token")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Valid // TODO: beanval isn't being honored here when used with MP Rest client
|
||||
public GithubTokenResponse accessToken(@QueryParam("client_id") String key,
|
||||
@QueryParam("client_secret") String secret,
|
||||
@QueryParam("code") String code,
|
||||
@QueryParam("redirect_uri") String redirect_uri,
|
||||
@QueryParam("state") String state);
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package org.libertybikes.auth.service.github;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
@RegisterRestClient
|
||||
@Path("/user")
|
||||
public interface GitHubUserAPI {
|
||||
|
||||
public static class EmailData {
|
||||
@Email
|
||||
public String email;
|
||||
public boolean primary;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/emails")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Valid // TODO: beanval isn't being called here when rest client instance is injected
|
||||
public EmailData[] getEmail(@QueryParam("access_token") String accessToken);
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package org.libertybikes.auth.service;
|
||||
package org.libertybikes.auth.service.google;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
@ -12,6 +12,7 @@ import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.libertybikes.auth.service.AuthApp;
|
||||
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
|
||||
import com.google.api.client.http.HttpTransport;
|
@ -1,4 +1,4 @@
|
||||
package org.libertybikes.auth.service;
|
||||
package org.libertybikes.auth.service.google;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
@ -20,6 +20,8 @@ import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.libertybikes.auth.service.AuthApp;
|
||||
import org.libertybikes.auth.service.JwtAuth;
|
||||
|
||||
import com.google.api.client.auth.oauth2.Credential;
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
|
@ -1,12 +1,14 @@
|
||||
<server>
|
||||
<featureManager>
|
||||
<feature>appSecurity-3.0</feature>
|
||||
<feature>beanValidation-2.0</feature>
|
||||
<feature>cdi-2.0</feature>
|
||||
<feature>jaxrs-2.1</feature>
|
||||
<feature>jndi-1.0</feature>
|
||||
<feature>jsonb-1.0</feature>
|
||||
<feature>mpConfig-1.2</feature>
|
||||
<feature>mpJwt-1.0</feature>
|
||||
<feature>mpRestClient-1.0</feature>
|
||||
</featureManager>
|
||||
|
||||
<httpEndpoint id="defaultHttpEndpoint" host="*" httpPort="${httpPort}" httpsPort="${httpsPort}" />
|
||||
|
@ -18,6 +18,9 @@
|
||||
<div class="form-item">
|
||||
<button type="button" (click)="loginGoogle()">Sign in with Google</button>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<button type="button" (click)="loginGithub()">Sign in with GitHub</button>
|
||||
</div>
|
||||
<h2 *ngIf="isFullDevice" class="section-header">or</h2>
|
||||
<div *ngIf="isFullDevice" class="form-item">
|
||||
<button type="button" (click)="hostRound()">Host Round</button>
|
||||
|
@ -72,6 +72,10 @@ export class LoginComponent implements OnInit, OnDestroy {
|
||||
window.location.href = `${environment.API_URL_AUTH}/auth-service/GoogleAuth`;
|
||||
}
|
||||
|
||||
loginGithub() {
|
||||
window.location.href = `${environment.API_URL_AUTH}/auth-service/GitHubAuth`;
|
||||
}
|
||||
|
||||
async quickJoin() {
|
||||
// First get an unstarted round ID
|
||||
let roundID = await this.http.get(`${environment.API_URL_GAME_ROUND}/available`, { responseType: 'text' }).toPromise();
|
||||
|
@ -9,7 +9,8 @@ public class Player {
|
||||
|
||||
public static enum DOMAIN {
|
||||
BASIC,
|
||||
GMAIL;
|
||||
GMAIL,
|
||||
GITHUB;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
@ -59,9 +60,10 @@ public class Player {
|
||||
|
||||
@JsonbTransient
|
||||
public DOMAIN getDomain() {
|
||||
if (id.startsWith(DOMAIN.GMAIL.toString()))
|
||||
return DOMAIN.GMAIL;
|
||||
else
|
||||
for (DOMAIN d : DOMAIN.values()) {
|
||||
if (id.startsWith(d.toString()))
|
||||
return d;
|
||||
}
|
||||
return DOMAIN.BASIC;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user