Token Endpoint

This commit is contained in:
Dusan Jakub
2023-09-15 14:06:27 +02:00
parent e703ca25a1
commit 38403ff828
8 changed files with 128 additions and 14 deletions

View File

@@ -40,6 +40,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-qute</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>

View File

@@ -1,10 +1,9 @@
package com.ysoft.geecon;
import com.ysoft.geecon.dto.AuthParams;
import com.ysoft.geecon.dto.OAuthClient;
import com.ysoft.geecon.dto.User;
import com.ysoft.geecon.dto.*;
import com.ysoft.geecon.error.OAuthException;
import com.ysoft.geecon.repo.ClientsRepo;
import com.ysoft.geecon.repo.SecureRandomStrings;
import com.ysoft.geecon.repo.SessionsRepo;
import com.ysoft.geecon.repo.UsersRepo;
import io.quarkus.qute.CheckedTemplate;
@@ -17,6 +16,7 @@ import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriBuilder;
import java.util.List;
import java.util.Optional;
@Path("/auth")
public class OAuthResource {
@@ -40,7 +40,7 @@ public class OAuthResource {
public TemplateInstance get(AuthParams params) {
var client = validateClient(params);
String sessionId = sessionsRepo.newAuthorizationSession(params, client);
return Templates.login(params.getLoginHint(), sessionId, "");
return Templates.login(params.getLoginHint(), sessionId, "");
}
@POST
@@ -75,7 +75,34 @@ public class OAuthResource {
.queryParam("state", params.getState())
.build())
.build();
}
}
@POST
@Path("/token")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public AccessTokenResponse token(TokenParams params) {
return switch (params.getGrantType()) {
case "authorization_code" -> redeemAuthorizationCode(params);
default -> throw new OAuthException("Unsupported grant type");
};
}
private AccessTokenResponse redeemAuthorizationCode(TokenParams params) {
validateClient(params);
var session = sessionsRepo.redeemAuthorizationCode(params.getCode())
.orElseThrow(() -> new OAuthException("Invalid code"));
String idToken = null;
return new AccessTokenResponse("Bearer",
8400,
SecureRandomStrings.alphanumeric(50),
session.scope(),
SecureRandomStrings.alphanumeric(50),
idToken
);
}
private User validateUser(String username, String password) {
return usersRepo.getUser(username)
@@ -95,6 +122,17 @@ public class OAuthResource {
return client;
}
private OAuthClient validateClient(TokenParams params) {
var client = clientsRepo.getClient(params.getClientId())
.orElseThrow(() -> new RuntimeException("Not a valid client"));
if (!client.validateRedirectUri(params.getRedirectUri())) {
throw new RuntimeException("Invalid redirect URI");
}
if (!client.validateSecret(params.getClientSecret())) {
throw new RuntimeException("Invalid secret");
}
return client;
}
}

View File

@@ -1,4 +1,4 @@
package com.ysoft.geecon.dto;
public record AccessTokenResponse(String token, String scope, String idToken, long expiresIn) {
}
public record AccessTokenResponse(String tokenType, long expiresIn, String accessToken, String scope, String refreshToken, String idToken) {
}

View File

@@ -6,14 +6,10 @@ import java.util.Arrays;
import java.util.List;
public class AuthParams {
public enum ResponseType {
code
}
@RestQuery("login_hint")
String loginHint;
@RestQuery("response_type")
ResponseType responseType;
String responseType;
@RestQuery("client_id")
String clientId;
@RestQuery("redirect_uri")
@@ -31,11 +27,11 @@ public class AuthParams {
this.loginHint = loginHint;
}
public ResponseType getResponseType() {
public String getResponseType() {
return responseType;
}
public void setResponseType(ResponseType responseType) {
public void setResponseType(String responseType) {
this.responseType = responseType;
}

View File

@@ -14,4 +14,8 @@ public record AuthorizationSession(AuthParams params, OAuthClient client, User u
public AuthorizationSession withScopes(List<String> acceptedScopes) {
return new AuthorizationSession(params, client, user, acceptedScopes);
}
public String scope() {
return acceptedScopes == null ? null : String.join(" ", acceptedScopes);
}
}

View File

@@ -1,7 +1,13 @@
package com.ysoft.geecon.dto;
import java.util.Objects;
public record OAuthClient(String clientId, String description, String clientSecret, String redirectUri) {
public boolean validateRedirectUri(String redirectUri) {
return this.redirectUri != null && this.redirectUri.equals(redirectUri);
}
public boolean validateSecret(String clientSecret) {
return Objects.equals(clientSecret, this.clientSecret);
}
}

View File

@@ -0,0 +1,65 @@
package com.ysoft.geecon.dto;
import jakarta.ws.rs.FormParam;
import org.jboss.resteasy.reactive.RestQuery;
import java.util.Arrays;
import java.util.List;
public class TokenParams {
@FormParam("grant_type")
private String grantType;
@FormParam("client_id")
private String clientId;
@FormParam("client_secret")
private String clientSecret;
@FormParam("redirect_uri")
private String redirectUri;
@FormParam("code")
private String code;
public String getGrantType() {
return grantType;
}
public void setGrantType(String grantType) {
this.grantType = grantType;
}
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 getRedirectUri() {
return redirectUri;
}
public void setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}

View File

@@ -14,6 +14,7 @@ public class ClientsRepo {
public ClientsRepo() {
register(new OAuthClient("my-public-client", "Example public client", null, "https://localhost:8888/oauth_success"));
register(new OAuthClient("oauthdebugger", "Example public client", null, "https://oauthdebugger.com/debug"));
}
public Optional<OAuthClient> getClient(String clientId) {