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 244 of file OAuthService.cpp.

247{
248 using namespace rapidjson;
249
250 Document document;
251 document.SetObject();
252
253 WriteCommonFields(document, "authorization_code", "all");
254
255 document.AddMember(
256 "code", StringRef(authorizationCode.data(), authorizationCode.size()),
257 document.GetAllocator());
258
259 const auto redirectURI = config.GetOAuthRedirectURL();
260
261 document.AddMember(
262 "redirect_uri", StringRef(redirectURI.data(), redirectURI.size()),
263 document.GetAllocator());
264
265 rapidjson::StringBuffer buffer;
266 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
267 document.Accept(writer);
268
270 config, { buffer.GetString(), buffer.GetSize() },
271 std::move(completedHandler));
272}
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 180 of file OAuthService.cpp.

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

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 233 of file OAuthService.cpp.

236{
237 std::lock_guard<std::recursive_mutex> lock(mMutex);
238
241 std::move(completedHandler));
242}
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:205
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 209 of file OAuthService.cpp.

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

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 295 of file OAuthService.cpp.

298{
299 using namespace audacity::network_manager;
300
301 Request request(config.GetAPIUrl("/auth/token"));
302
303 request.setHeader(
305
306 request.setHeader(
308
309 auto response = NetworkManager::GetInstance().doPost(
310 request, payload.data(), payload.size());
311
312 response->setRequestFinishedCallback(
313 [response, this, handler = std::move(completedHandler)](auto)
314 {
315 const auto httpCode = response->getHTTPCode();
316 const auto body = response->readAll<std::string>();
317
318 if (httpCode != 200)
319 {
320 if (handler)
321 handler({});
322
323 // Token has expired?
324 if (httpCode == 422)
325 BasicUI::CallAfter([this] { UnlinkAccount(); });
326 else
327 SafePublish({ {}, body, false });
328
329 return;
330 }
331
332 rapidjson::Document document;
333 document.Parse(body.data(), body.size());
334
335 if (!document.IsObject())
336 {
337 if (handler)
338 handler({});
339
340 SafePublish({ {}, body, false });
341 return;
342 }
343
344 const auto tokenType = document["token_type"].GetString();
345 const auto accessToken = document["access_token"].GetString();
346 const auto expiresIn = document["expires_in"].GetInt64();
347 const auto newRefreshToken = document["refresh_token"].GetString();
348
349 {
350 std::lock_guard<std::recursive_mutex> lock(mMutex);
351
352 mAccessToken = std::string(tokenType) + " " + accessToken;
354 Clock::now() + std::chrono::seconds(expiresIn);
355 }
356
358 [token = std::string(newRefreshToken)]()
359 {
360 // At this point access token is already written,
361 // only refresh token is updated.
362 refreshToken.Write(token);
363 gPrefs->Flush();
364 });
365
366 if (handler)
368
369 // The callback only needs the access token, so invoke it immediately.
370 // Networking is thread safe
371 SafePublish({ mAccessToken, {}, true });
372 });
373}
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:257
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 285 of file OAuthService.cpp.

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

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 95 of file OAuthService.cpp.

97{
98 if (!IsPrefixed(uri, uriPrefix))
99 {
100 if (completedHandler)
101 completedHandler({});
102 return;
103 }
104
105 // It was observed, that sometimes link is passed as audacity://link/
106 // This is valid from URI point of view, but we need to handle it separately
107 const auto argsStart = uri.find("?");
108
109 if (argsStart == std::string_view::npos)
110 {
111 if (completedHandler)
112 completedHandler({});
113 return;
114 }
115
116 // Length is handled in IsPrefixed
117 auto args = uri.substr(argsStart + 1);
118
119 std::string_view token;
120 std::string_view username;
121 std::string_view password;
122 std::string_view authorizationCode;
123
124 while (!args.empty())
125 {
126 const auto nextArg = args.find('&');
127
128 const auto arg = args.substr(0, nextArg);
129 args = nextArg == std::string_view::npos ? "" : args.substr(nextArg + 1);
130
131 if (IsPrefixed(arg, usernamePrefix))
132 username = arg.substr(usernamePrefix.length());
133 else if (IsPrefixed(arg, passwordPrefix))
134 password = arg.substr(passwordPrefix.length());
135 else if (IsPrefixed(arg, tokenPrefix))
136 token = arg.substr(tokenPrefix.length());
137 else if (IsPrefixed(arg, authorizationCodePrefix))
138 authorizationCode = arg.substr(authorizationCodePrefix.length());
139 }
140
141 // We have a prioritized list of authorization methods
142 if (!authorizationCode.empty())
143 {
145 GetServiceConfig(), authorizationCode, std::move(completedHandler));
146 }
147 else if (!token.empty())
148 {
150 GetServiceConfig(), token, std::move(completedHandler));
151 }
152 else if (!username.empty() && !password.empty())
153 {
156 audacity::UrlDecode(std::string(username)),
157 audacity::UrlDecode(std::string(password)),
158 std::move(completedHandler));
159 }
160 else
161 {
162 if (completedHandler)
163 completedHandler({});
164 }
165}
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 274 of file OAuthService.cpp.

275{
276 return !GetAccessToken().empty();
277}
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 279 of file OAuthService.cpp.

280{
281 std::lock_guard<std::recursive_mutex> lock(mMutex);
282 return !refreshToken.Read().empty();
283}

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 375 of file OAuthService.cpp.

376{
377 BasicUI::CallAfter([this, message]() { Publish(message); });
378}
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 167 of file OAuthService.cpp.

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

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 82 of file OAuthService.cpp.

84{
86 {
87 if (completedHandler)
88 completedHandler(GetAccessToken());
89 return;
90 }
91
92 AuthoriseRefreshToken(GetServiceConfig(), std::move(completedHandler));
93}
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: