Audacity 3.2.0
Public Member Functions | Private Types | Private Member Functions | Private Attributes | List of all members
cloud::audiocom::OAuthService Class Referencefinal

Service responsible for OAuth authentication against the audio.com service. More...

#include <OAuthService.h>

Inheritance diagram for cloud::audiocom::OAuthService:
[legend]
Collaboration diagram for cloud::audiocom::OAuthService:
[legend]

Public Member Functions

bool HasAccessToken () const
 Indicates, that service has a valid access token, i. e. that the user is authorized. More...
 
bool HasRefreshToken () const
 
void ValidateAuth (std::function< void(std::string_view)> completedHandler)
 Attempt to authorize the user. More...
 
void HandleLinkURI (std::string_view uri, std::function< void(std::string_view)> completedHandler)
 Handle the OAuth callback. More...
 
void UnlinkAccount ()
 Removes access and refresh token, notifies about the logout. More...
 
std::string GetAccessToken () const
 Return the current access token, if any. More...
 
- Public Member Functions inherited from Observer::Publisher< AuthStateChangedMessage >
 Publisher (ExceptionPolicy *pPolicy=nullptr, Alloc a={})
 Constructor supporting type-erased custom allocation/deletion. More...
 
 Publisher (Publisher &&)=default
 
Publisheroperator= (Publisher &&)=default
 
Subscription Subscribe (Callback callback)
 Connect a callback to the Publisher; later-connected are called earlier. More...
 
Subscription Subscribe (Object &obj, Return(Object::*callback)(Args...))
 Overload of Subscribe takes an object and pointer-to-member-function. More...
 

Private Types

using Clock = std::chrono::steady_clock
 

Private Member Functions

void AuthorisePassword (const ServiceConfig &config, std::string_view userName, std::string_view password, std::function< void(std::string_view)> completedHandler)
 
void AuthoriseRefreshToken (const ServiceConfig &config, std::string_view refreshToken, std::function< void(std::string_view)> completedHandler)
 
void AuthoriseRefreshToken (const ServiceConfig &config, std::function< void(std::string_view)> completedHandler)
 
void AuthoriseCode (const ServiceConfig &config, std::string_view authorizationCode, std::function< void(std::string_view)> completedHandler)
 
void DoAuthorise (const ServiceConfig &config, std::string_view payload, std::function< void(std::string_view)> completedHandler)
 
void SafePublish (const AuthStateChangedMessage &message)
 

Private Attributes

std::recursive_mutex mMutex
 
Clock::time_point mTokenExpirationTime
 
std::string mAccessToken
 

Additional Inherited Members

- Public Types inherited from Observer::Publisher< AuthStateChangedMessage >
using message_type = AuthStateChangedMessage
 
using CallbackReturn = std::conditional_t< true, void, bool >
 
using Callback = std::function< CallbackReturn(const AuthStateChangedMessage &) >
 Type of functions that can be connected to the Publisher. More...
 
- Static Public Attributes inherited from Observer::Publisher< AuthStateChangedMessage >
static constexpr bool notifies_all
 
- Protected Member Functions inherited from Observer::Publisher< AuthStateChangedMessage >
CallbackReturn Publish (const AuthStateChangedMessage &message)
 Send a message to connected callbacks. More...
 

Detailed Description

Service responsible for OAuth authentication against the audio.com service.

Definition at line 36 of file OAuthService.h.

Member Typedef Documentation

◆ Clock

using cloud::audiocom::OAuthService::Clock = std::chrono::steady_clock
private

Definition at line 102 of file OAuthService.h.

Member Function Documentation

◆ AuthoriseCode()

void cloud::audiocom::OAuthService::AuthoriseCode ( const ServiceConfig config,
std::string_view  authorizationCode,
std::function< void(std::string_view)>  completedHandler 
)
private

Definition at line 247 of file OAuthService.cpp.

250{
251 using namespace rapidjson;
252
253 Document document;
254 document.SetObject();
255
256 WriteCommonFields(document, "authorization_code", "all");
257
258 document.AddMember(
259 "code", StringRef(authorizationCode.data(), authorizationCode.size()),
260 document.GetAllocator());
261
262 const auto redirectURI = config.GetOAuthRedirectURL();
263
264 document.AddMember(
265 "redirect_uri", StringRef(redirectURI.data(), redirectURI.size()),
266 document.GetAllocator());
267
268 rapidjson::StringBuffer buffer;
269 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
270 document.Accept(writer);
271
273 config, { buffer.GetString(), buffer.GetSize() },
274 std::move(completedHandler));
275}
void DoAuthorise(const ServiceConfig &config, std::string_view payload, std::function< void(std::string_view)> completedHandler)
void WriteCommonFields(rapidjson::Document &document, std::string_view grantType, std::string_view scope)

References DoAuthorise(), cloud::audiocom::ServiceConfig::GetOAuthRedirectURL(), and cloud::audiocom::anonymous_namespace{OAuthService.cpp}::WriteCommonFields().

Referenced by HandleLinkURI().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ AuthorisePassword()

void cloud::audiocom::OAuthService::AuthorisePassword ( const ServiceConfig config,
std::string_view  userName,
std::string_view  password,
std::function< void(std::string_view)>  completedHandler 
)
private

Definition at line 183 of file OAuthService.cpp.

187{
188 using namespace rapidjson;
189
190 Document document;
191 document.SetObject();
192
193 WriteCommonFields(document, "password", "all");
194
195 document.AddMember(
196 "username", StringRef(userName.data(), userName.size()),
197 document.GetAllocator());
198
199 document.AddMember(
200 "password", StringRef(password.data(), password.size()),
201 document.GetAllocator());
202
203 rapidjson::StringBuffer buffer;
204 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
205 document.Accept(writer);
206
208 config, { buffer.GetString(), buffer.GetSize() },
209 std::move(completedHandler));
210}

References DoAuthorise(), cloud::audiocom::anonymous_namespace{UserService.cpp}::userName, and cloud::audiocom::anonymous_namespace{OAuthService.cpp}::WriteCommonFields().

Referenced by HandleLinkURI().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ AuthoriseRefreshToken() [1/2]

void cloud::audiocom::OAuthService::AuthoriseRefreshToken ( const ServiceConfig config,
std::function< void(std::string_view)>  completedHandler 
)
private

Definition at line 236 of file OAuthService.cpp.

239{
240 std::lock_guard<std::recursive_mutex> lock(mMutex);
241
244 std::move(completedHandler));
245}
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:207
void AuthoriseRefreshToken(const ServiceConfig &config, std::string_view refreshToken, std::function< void(std::string_view)> completedHandler)
std::recursive_mutex mMutex
Definition: OAuthService.h:104
std::string ToUTF8(const std::wstring &wstr)

References AuthoriseRefreshToken(), mMutex, Setting< T >::Read(), cloud::audiocom::anonymous_namespace{OAuthService.cpp}::refreshToken, and audacity::ToUTF8().

Here is the call graph for this function:

◆ AuthoriseRefreshToken() [2/2]

void cloud::audiocom::OAuthService::AuthoriseRefreshToken ( const ServiceConfig config,
std::string_view  refreshToken,
std::function< void(std::string_view)>  completedHandler 
)
private

Definition at line 212 of file OAuthService.cpp.

215{
216 using namespace rapidjson;
217
218 Document document;
219 document.SetObject();
220
221 WriteCommonFields(document, "refresh_token", "");
222
223 document.AddMember(
224 "refresh_token", StringRef(token.data(), token.size()),
225 document.GetAllocator());
226
227 rapidjson::StringBuffer buffer;
228 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
229 document.Accept(writer);
230
232 config, { buffer.GetString(), buffer.GetSize() },
233 std::move(completedHandler));
234}

References DoAuthorise(), and cloud::audiocom::anonymous_namespace{OAuthService.cpp}::WriteCommonFields().

Referenced by AuthoriseRefreshToken(), HandleLinkURI(), and ValidateAuth().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ DoAuthorise()

void cloud::audiocom::OAuthService::DoAuthorise ( const ServiceConfig config,
std::string_view  payload,
std::function< void(std::string_view)>  completedHandler 
)
private

Definition at line 298 of file OAuthService.cpp.

301{
302 using namespace audacity::network_manager;
303
304 Request request(config.GetAPIUrl("/auth/token"));
305
306 request.setHeader(
308
309 request.setHeader(
311
312 auto response = NetworkManager::GetInstance().doPost(
313 request, payload.data(), payload.size());
314
315 response->setRequestFinishedCallback(
316 [response, this, handler = std::move(completedHandler)](auto)
317 {
318 const auto httpCode = response->getHTTPCode();
319 const auto body = response->readAll<std::string>();
320
321 if (httpCode != 200)
322 {
323 if (handler)
324 handler({});
325
326 // Token has expired?
327 if (httpCode == 422)
328 BasicUI::CallAfter([this] { UnlinkAccount(); });
329 else
330 SafePublish({ {}, body, false });
331
332 return;
333 }
334
335 rapidjson::Document document;
336 document.Parse(body.data(), body.size());
337
338 if (!document.IsObject())
339 {
340 if (handler)
341 handler({});
342
343 SafePublish({ {}, body, false });
344 return;
345 }
346
347 const auto tokenType = document["token_type"].GetString();
348 const auto accessToken = document["access_token"].GetString();
349 const auto expiresIn = document["expires_in"].GetInt64();
350 const auto newRefreshToken = document["refresh_token"].GetString();
351
352 {
353 std::lock_guard<std::recursive_mutex> lock(mMutex);
354
355 mAccessToken = std::string(tokenType) + " " + accessToken;
357 Clock::now() + std::chrono::seconds(expiresIn);
358 }
359
361 [token = std::string(newRefreshToken)]()
362 {
363 // At this point access token is already written,
364 // only refresh token is updated.
365 refreshToken.Write(token);
366 gPrefs->Flush();
367 });
368
369 if (handler)
371
372 // The callback only needs the access token, so invoke it immediately.
373 // Networking is thread safe
374 SafePublish({ mAccessToken, {}, true });
375 });
376}
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:259
virtual bool Flush() noexcept=0
void UnlinkAccount()
Removes access and refresh token, notifies about the logout.
Clock::time_point mTokenExpirationTime
Definition: OAuthService.h:106
void SafePublish(const AuthStateChangedMessage &message)
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:208
FrameStatistics & GetInstance() noexcept

References audacity::network_manager::common_headers::Accept, audacity::network_manager::common_content_types::ApplicationJson, BasicUI::CallAfter(), audacity::network_manager::common_headers::ContentType, audacity::BasicSettings::Flush(), cloud::audiocom::ServiceConfig::GetAPIUrl(), anonymous_namespace{FrameStatistics.cpp}::GetInstance(), gPrefs, cloud::audiocom::anonymous_namespace{AuthorizationHandler.cpp}::handler, mAccessToken, mMutex, mTokenExpirationTime, cloud::audiocom::anonymous_namespace{OAuthService.cpp}::refreshToken, SafePublish(), audacity::network_manager::Request::setHeader(), UnlinkAccount(), and Setting< T >::Write().

Referenced by AuthoriseCode(), AuthorisePassword(), and AuthoriseRefreshToken().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetAccessToken()

std::string cloud::audiocom::OAuthService::GetAccessToken ( ) const

Return the current access token, if any.

Definition at line 288 of file OAuthService.cpp.

289{
290 std::lock_guard<std::recursive_mutex> lock(mMutex);
291
292 if (Clock::now() < mTokenExpirationTime)
293 return mAccessToken;
294
295 return {};
296}

References mAccessToken, mMutex, and mTokenExpirationTime.

Referenced by HasAccessToken(), and ValidateAuth().

Here is the caller graph for this function:

◆ HandleLinkURI()

void cloud::audiocom::OAuthService::HandleLinkURI ( std::string_view  uri,
std::function< void(std::string_view)>  completedHandler 
)

Handle the OAuth callback.

Definition at line 98 of file OAuthService.cpp.

100{
101 if (!IsPrefixed(uri, uriPrefix))
102 {
103 if (completedHandler)
104 completedHandler({});
105 return;
106 }
107
108 // It was observed, that sometimes link is passed as audacity://link/
109 // This is valid from URI point of view, but we need to handle it separately
110 const auto argsStart = uri.find("?");
111
112 if (argsStart == std::string_view::npos)
113 {
114 if (completedHandler)
115 completedHandler({});
116 return;
117 }
118
119 // Length is handled in IsPrefixed
120 auto args = uri.substr(argsStart + 1);
121
122 std::string_view token;
123 std::string_view username;
124 std::string_view password;
125 std::string_view authorizationCode;
126
127 while (!args.empty())
128 {
129 const auto nextArg = args.find('&');
130
131 const auto arg = args.substr(0, nextArg);
132 args = nextArg == std::string_view::npos ? "" : args.substr(nextArg + 1);
133
134 if (IsPrefixed(arg, usernamePrefix))
135 username = arg.substr(usernamePrefix.length());
136 else if (IsPrefixed(arg, passwordPrefix))
137 password = arg.substr(passwordPrefix.length());
138 else if (IsPrefixed(arg, tokenPrefix))
139 token = arg.substr(tokenPrefix.length());
140 else if (IsPrefixed(arg, authorizationCodePrefix))
141 authorizationCode = arg.substr(authorizationCodePrefix.length());
142 }
143
144 // We have a prioritized list of authorization methods
145 if (!authorizationCode.empty())
146 {
148 GetServiceConfig(), authorizationCode, std::move(completedHandler));
149 }
150 else if (!token.empty())
151 {
153 GetServiceConfig(), token, std::move(completedHandler));
154 }
155 else if (!username.empty() && !password.empty())
156 {
159 audacity::UrlDecode(std::string(username)),
160 audacity::UrlDecode(std::string(password)),
161 std::move(completedHandler));
162 }
163 else
164 {
165 if (completedHandler)
166 completedHandler({});
167 }
168}
void AuthoriseCode(const ServiceConfig &config, std::string_view authorizationCode, std::function< void(std::string_view)> completedHandler)
void AuthorisePassword(const ServiceConfig &config, std::string_view userName, std::string_view password, std::function< void(std::string_view)> completedHandler)
constexpr size_t npos(-1)
std::string UrlDecode(const std::string &url)
Definition: UrlDecode.cpp:18
bool IsPrefixed(std::string_view hay, std::string_view prefix)
const ServiceConfig & GetServiceConfig()
Returns the instance of the ServiceConfig.

References AuthoriseCode(), AuthorisePassword(), AuthoriseRefreshToken(), cloud::audiocom::anonymous_namespace{OAuthService.cpp}::authorizationCodePrefix, cloud::audiocom::GetServiceConfig(), cloud::audiocom::anonymous_namespace{OAuthService.cpp}::IsPrefixed(), Tuple::detail::npos(), cloud::audiocom::anonymous_namespace{OAuthService.cpp}::passwordPrefix, cloud::audiocom::anonymous_namespace{OAuthService.cpp}::tokenPrefix, cloud::audiocom::anonymous_namespace{OAuthService.cpp}::uriPrefix, audacity::UrlDecode(), and cloud::audiocom::anonymous_namespace{OAuthService.cpp}::usernamePrefix.

Referenced by cloud::audiocom::LinkAccountDialog::OnContinue().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ HasAccessToken()

bool cloud::audiocom::OAuthService::HasAccessToken ( ) const

Indicates, that service has a valid access token, i. e. that the user is authorized.

Definition at line 277 of file OAuthService.cpp.

278{
279 return !GetAccessToken().empty();
280}
std::string GetAccessToken() const
Return the current access token, if any.

References GetAccessToken().

Referenced by ValidateAuth().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ HasRefreshToken()

bool cloud::audiocom::OAuthService::HasRefreshToken ( ) const

Indicates, that the service has a possibly valid refresh token, which can be used to authorize the user

Definition at line 282 of file OAuthService.cpp.

283{
284 std::lock_guard<std::recursive_mutex> lock(mMutex);
285 return !refreshToken.Read().empty();
286}

References mMutex, Setting< T >::Read(), and cloud::audiocom::anonymous_namespace{OAuthService.cpp}::refreshToken.

Referenced by ValidateAuth().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ SafePublish()

void cloud::audiocom::OAuthService::SafePublish ( const AuthStateChangedMessage message)
private

Definition at line 378 of file OAuthService.cpp.

379{
380 BasicUI::CallAfter([this, message]() { Publish(message); });
381}
CallbackReturn Publish(const AuthStateChangedMessage &message)
Send a message to connected callbacks.
Definition: Observer.h:207

References BasicUI::CallAfter(), and Observer::Publisher< AuthStateChangedMessage >::Publish().

Referenced by DoAuthorise().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ UnlinkAccount()

void cloud::audiocom::OAuthService::UnlinkAccount ( )

Removes access and refresh token, notifies about the logout.

Definition at line 170 of file OAuthService.cpp.

171{
172 std::lock_guard<std::recursive_mutex> lock(mMutex);
173
174 mAccessToken.clear();
176 gPrefs->Flush();
177
178 // Unlink account is expected to be called only
179 // on UI thread
180 Publish({ {}, {}, false });
181}

References audacity::BasicSettings::Flush(), gPrefs, mAccessToken, mMutex, Observer::Publisher< AuthStateChangedMessage >::Publish(), cloud::audiocom::anonymous_namespace{OAuthService.cpp}::refreshToken, and Setting< T >::Write().

Referenced by DoAuthorise().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ValidateAuth()

void cloud::audiocom::OAuthService::ValidateAuth ( std::function< void(std::string_view)>  completedHandler)

Attempt to authorize the user.

Definition at line 85 of file OAuthService.cpp.

87{
89 {
90 if (completedHandler)
91 completedHandler(GetAccessToken());
92 return;
93 }
94
95 AuthoriseRefreshToken(GetServiceConfig(), std::move(completedHandler));
96}
bool HasAccessToken() const
Indicates, that service has a valid access token, i. e. that the user is authorized.

References AuthoriseRefreshToken(), GetAccessToken(), cloud::audiocom::GetServiceConfig(), HasAccessToken(), and HasRefreshToken().

Referenced by cloud::audiocom::UploadService::Upload().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ mAccessToken

std::string cloud::audiocom::OAuthService::mAccessToken
private

Definition at line 107 of file OAuthService.h.

Referenced by DoAuthorise(), GetAccessToken(), and UnlinkAccount().

◆ mMutex

std::recursive_mutex cloud::audiocom::OAuthService::mMutex
mutableprivate

◆ mTokenExpirationTime

Clock::time_point cloud::audiocom::OAuthService::mTokenExpirationTime
private

Definition at line 106 of file OAuthService.h.

Referenced by DoAuthorise(), and GetAccessToken().


The documentation for this class was generated from the following files: