View Javadoc
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 }