1 /*
2  * Copyright (C) 2017 Emweb bv, Herent, Belgium.
3  *
4  * See the LICENSE file for terms of use.
5  */
6 #include "OidcUserInfoEndpoint.h"
7 
8 #include <string>
9 #include <boost/algorithm/string.hpp>
10 
11 #include "Wt/WResource.h"
12 #include "Wt/WObject.h"
13 #include "Wt/Http/Request.h"
14 #include "Wt/Http/Response.h"
15 #include "Wt/Auth/User.h"
16 #include "Wt/Auth/AbstractUserDatabase.h"
17 #include "Wt/Auth/IssuedToken.h"
18 #include "Wt/Http/Request.h"
19 #include "Wt/Http/Response.h"
20 #include "Wt/Json/Value.h"
21 #include "Wt/Json/Object.h"
22 #include "Wt/Json/Serializer.h"
23 #include "Wt/WLogger.h"
24 
25 namespace {
26 const std::string AUTH_TYPE = "Bearer ";
27 }
28 
29 namespace Wt {
30 
31   LOGGER("OidcUserInfoEndpoint");
32 
33 namespace Auth {
34 
35 
OidcUserInfoEndpoint(AbstractUserDatabase & db)36 OidcUserInfoEndpoint::OidcUserInfoEndpoint(AbstractUserDatabase &db)
37   : db_(&db)
38 {
39   std::set<std::string> s1;
40   s1.insert("name");
41   setScopeToken("profile", s1);
42   std::set<std::string> s2;
43   s2.insert("email");
44   s2.insert("email_verified");
45   setScopeToken("email", s2);
46 }
47 
~OidcUserInfoEndpoint()48 OidcUserInfoEndpoint::~OidcUserInfoEndpoint()
49 {
50   beingDeleted();
51 }
52 
handleRequest(const Http::Request & request,Http::Response & response)53 void OidcUserInfoEndpoint::handleRequest(const Http::Request& request, Http::Response& response)
54 {
55   std::string authHeader = request.headerValue("Authorization");
56   if (!boost::starts_with(authHeader, AUTH_TYPE)) {
57     response.setStatus(400);
58     response.addHeader("WWW-Authenticate", "error=\"invalid_request\"");
59     LOG_INFO("error=\"invalid_request\": Authorization header missing");
60     return;
61   }
62   std::string tokenValue = authHeader.substr(AUTH_TYPE.length());
63   IssuedToken accessToken = db_->idpTokenFindWithValue("access_token", tokenValue);
64   if (!accessToken.checkValid() || WDateTime::currentDateTime() > accessToken.expirationTime()) {
65     response.setStatus(401);
66     response.addHeader("WWW-Authenticate", "error=\"invalid_token\"");
67     LOG_INFO("error=\"invalid_token\" " << authHeader);
68     return;
69   }
70   response.setMimeType("application/json");
71   response.setStatus(200);
72   User user = accessToken.user();
73   std::string scope = accessToken.scope();
74   std::set<std::string> scopeSet;
75   boost::split(scopeSet, scope, boost::is_any_of(" "));
76 #ifdef WT_TARGET_JAVA
77   try {
78 #endif
79     response.out() << Json::serialize(generateUserInfo(user, scopeSet)) << std::endl;
80     LOG_INFO("Response sent for " << user.id() << "(" << db_->email(user) << ")");
81 #ifdef WT_TARGET_JAVA
82   } catch (std::io_exception ioe) {
83     LOG_ERROR(ioe.message());
84   }
85 #endif
86 }
87 
generateUserInfo(const User & user,const std::set<std::string> & scope)88 Json::Object OidcUserInfoEndpoint::generateUserInfo(const User& user, const std::set<std::string>& scope)
89 {
90   Json::Object root;
91   root["sub"] = Json::Value(user.id());
92   std::set<std::string> claims;
93   for (std::set<std::string>::iterator s = scope.begin(); s != scope.end(); ++s) {
94     std::map<std::string,std::set<std::string> >::const_iterator it
95       = claimMap_.find(*s);
96     if (it == claimMap_.end())
97       continue;
98 
99     const std::set<std::string>& c = it->second;
100     for (std::set<std::string>::iterator s2 = c.begin(); s2 != c.end(); ++s2)
101       claims.insert(*s2);
102   }
103   for (std::set<std::string>::iterator claim = claims.begin(); claim != claims.end(); ++claim) {
104     Json::Value claimValue = db_->idpJsonClaim(user, *claim);
105     if (!claimValue.isNull())
106       root[*claim] = claimValue;
107   }
108   return root;
109 }
110 
setScopeToken(const std::string & scopeToken,const std::set<std::string> & claims)111 void OidcUserInfoEndpoint::setScopeToken(const std::string& scopeToken,
112                                          const std::set<std::string>& claims)
113 {
114   claimMap_[scopeToken] = claims;
115 }
116 
117 const std::map<std::string,std::set<std::string> > &
scopeTokens()118 OidcUserInfoEndpoint::scopeTokens() const
119 {
120   return claimMap_;
121 }
122 
123 }
124 }
125