1 package de.dlr.shepard.auth.security;
2
3 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 import com.fasterxml.jackson.annotation.JsonProperty;
5 import de.dlr.shepard.common.exceptions.ShepardProcessingException;
6 import io.quarkus.logging.Log;
7 import jakarta.enterprise.context.RequestScoped;
8 import jakarta.ws.rs.ProcessingException;
9 import jakarta.ws.rs.WebApplicationException;
10 import jakarta.ws.rs.client.Client;
11 import jakarta.ws.rs.client.ClientBuilder;
12 import jakarta.ws.rs.core.HttpHeaders;
13 import jakarta.ws.rs.core.MediaType;
14 import java.net.URI;
15 import java.net.URISyntaxException;
16 import lombok.AllArgsConstructor;
17 import lombok.Data;
18 import lombok.NoArgsConstructor;
19 import org.eclipse.microprofile.config.ConfigProvider;
20
21 @Data
22 @AllArgsConstructor
23 @NoArgsConstructor
24 @JsonIgnoreProperties(ignoreUnknown = true)
25 class OpenIdConfiguration {
26
27 private String issuer;
28
29 @JsonProperty("authorization_endpoint")
30 private String authorizationEndpoint;
31
32 @JsonProperty("userinfo_endpoint")
33 private String userinfoEndpoint;
34
35 @JsonProperty("jwks_uri")
36 private String jwksUri;
37
38 @JsonProperty("response_types_supported")
39 private String[] responseTypesSupported;
40
41 @JsonProperty("subject_types_supported")
42 private String[] subjectTypesSupported;
43
44 @JsonProperty("id_token_signing_alg_values_supported")
45 private String[] idTokenSigningAlgValuesSupported;
46 }
47
48 @RequestScoped
49 public class UserinfoService {
50
51 private static final String WELL_KNOWN_PATH = ".well-known/openid-configuration";
52
53 private final URI oidcConfigurationURI;
54 private String userinfoEndpoint;
55
56 private final Client client = ClientBuilder.newClient();
57
58 public UserinfoService() {
59 String oidcAuthority = ConfigProvider.getConfig().getValue("oidc.authority", String.class);
60 try {
61 URI base = new URI(oidcAuthority + "/").normalize();
62 oidcConfigurationURI = base.resolve(WELL_KNOWN_PATH);
63 } catch (URISyntaxException e) {
64 throw new ShepardProcessingException(e.getMessage());
65 }
66 }
67
68 protected void init() {
69 var openIdConfiguration = getOpenIdConfiguration();
70 if (openIdConfiguration == null) {
71 throw new ShepardProcessingException("Could not fetch openid configuration");
72 }
73 userinfoEndpoint = openIdConfiguration.getUserinfoEndpoint();
74 }
75
76 public Userinfo fetchUserinfo(String accessToken) {
77 if (userinfoEndpoint == null) {
78 init();
79 }
80
81 var userinfo = getUserinfo(userinfoEndpoint, accessToken);
82 if (userinfo == null) {
83 throw new ShepardProcessingException("Could not fetch userinfo");
84 }
85 return userinfo;
86 }
87
88 private OpenIdConfiguration getOpenIdConfiguration() {
89 var request = client.target(oidcConfigurationURI).request(MediaType.APPLICATION_JSON).buildGet();
90
91 OpenIdConfiguration response;
92 try {
93 response = request.invoke(OpenIdConfiguration.class);
94 } catch (ProcessingException | WebApplicationException e) {
95 Log.errorf("Request was unsuccessful: URI: %s, Error: %s", oidcConfigurationURI.toString(), e.getMessage());
96 return null;
97 }
98 return response;
99 }
100
101 private Userinfo getUserinfo(String uri, String bearer) {
102 var request = client
103 .target(uri)
104 .request(MediaType.APPLICATION_JSON)
105 .header(HttpHeaders.AUTHORIZATION, bearer)
106 .buildGet();
107
108 Userinfo response;
109 try {
110 response = request.invoke(Userinfo.class);
111 } catch (ProcessingException | WebApplicationException e) {
112 Log.errorf("Request was unsuccessful: URI: %s, Error: %s", uri, e.getMessage());
113 return null;
114 }
115 return response;
116 }
117 }