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