Skip to content

Commit aa8dc80

Browse files
Add MtlsTokenProvider for mTLS authentication
Implementing auto refreshing in place not using AutoRefreshToken and TokenEndpoint for simplicity and because mtls endpoint has very simple list of arguments and some features implemented in client authentication may be not supported at the moment. Relates-To: DATASDK-105 Signed-off-by: Rustam Gamidov <ext-rustam.gamidov@here.com>
1 parent 6363d4f commit aa8dc80

15 files changed

Lines changed: 1149 additions & 37 deletions

examples/CMakeLists.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ set(OLP_SDK_DATASERVICE_READ_EXAMPLE_TARGET dataservice-read-example)
2020
set(OLP_SDK_DATASERVICE_WRITE_EXAMPLE_TARGET dataservice-write-example)
2121
set(OLP_SDK_DATASERVICE_CACHE_EXAMPLE_TARGET dataservice-cache-example)
2222
set(OLP_SDK_DATASERVICE_READ_STREAM_LAYER_EXAMPLE_TARGET dataservice-read-stream-layer-example)
23+
set(OLP_SDK_MTLS_AUTHENTICATION_EXAMPLE_TARGET mtls-authentication-example)
2324

2425
set(OLP_SDK_EXAMPLE_SUCCESS_STRING "Example has finished successfully")
2526
set(OLP_SDK_EXAMPLE_FAILURE_STRING "Example failed!")
@@ -81,6 +82,14 @@ else()
8182
olp-cpp-sdk-authentication
8283
olp-cpp-sdk-dataservice-read)
8384

85+
add_library(${OLP_SDK_MTLS_AUTHENTICATION_EXAMPLE_TARGET}
86+
./MtlsAuthenticationExample.cpp
87+
./Examples.h
88+
./MtlsAuthenticationExample.h)
89+
90+
target_link_libraries(${OLP_SDK_MTLS_AUTHENTICATION_EXAMPLE_TARGET}
91+
olp-cpp-sdk-authentication)
92+
8493
target_compile_definitions(${OLP_SDK_DATASERVICE_READ_EXAMPLE_TARGET}
8594
PRIVATE EXAMPLES_LIBRARY)
8695
target_compile_definitions(${OLP_SDK_DATASERVICE_WRITE_EXAMPLE_TARGET}
@@ -89,6 +98,8 @@ else()
8998
PRIVATE EXAMPLES_LIBRARY)
9099
target_compile_definitions(${OLP_SDK_DATASERVICE_READ_STREAM_LAYER_EXAMPLE_TARGET}
91100
PRIVATE EXAMPLES_LIBRARY)
101+
target_compile_definitions(${OLP_SDK_MTLS_AUTHENTICATION_EXAMPLE_TARGET}
102+
PRIVATE EXAMPLES_LIBRARY)
92103
if(BUILD_SHARED_LIBS)
93104
target_compile_definitions(${OLP_SDK_DATASERVICE_READ_EXAMPLE_TARGET}
94105
PUBLIC EXAMPLES_SHARED_LIBRARY)
@@ -98,6 +109,8 @@ else()
98109
PUBLIC EXAMPLES_SHARED_LIBRARY)
99110
target_compile_definitions(${OLP_SDK_DATASERVICE_READ_STREAM_LAYER_EXAMPLE_TARGET}
100111
PUBLIC EXAMPLES_SHARED_LIBRARY)
112+
target_compile_definitions(${OLP_SDK_MTLS_AUTHENTICATION_EXAMPLE_TARGET}
113+
PUBLIC EXAMPLES_SHARED_LIBRARY)
101114
endif()
102115

103116
add_executable(${OLP_SDK_DATASERVICE_EXAMPLE_TARGET}
@@ -109,6 +122,7 @@ else()
109122
${OLP_SDK_DATASERVICE_READ_EXAMPLE_TARGET}
110123
${OLP_SDK_DATASERVICE_WRITE_EXAMPLE_TARGET}
111124
${OLP_SDK_DATASERVICE_CACHE_EXAMPLE_TARGET}
112-
${OLP_SDK_DATASERVICE_READ_STREAM_LAYER_EXAMPLE_TARGET})
125+
${OLP_SDK_DATASERVICE_READ_STREAM_LAYER_EXAMPLE_TARGET}
126+
${OLP_SDK_MTLS_AUTHENTICATION_EXAMPLE_TARGET})
113127

114128
endif()
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/*
2+
* Copyright (C) 2026 HERE Europe B.V.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* License-Filename: LICENSE
18+
*/
19+
20+
#include "MtlsAuthenticationExample.h"
21+
22+
#include <olp/authentication/MtlsTokenProvider.h>
23+
#include <olp/core/client/OlpClientSettingsFactory.h>
24+
#include <olp/core/http/HttpStatusCode.h>
25+
#include <olp/core/http/Network.h>
26+
#include <olp/core/logging/Log.h>
27+
#include <olp/core/utils/Url.h>
28+
29+
#include <fstream>
30+
#include <future>
31+
#include <map>
32+
#include <memory>
33+
#include <sstream>
34+
#include <string>
35+
#include <utility>
36+
37+
namespace {
38+
constexpr auto kLogTag = "mtls-authentication-example";
39+
40+
constexpr auto kDiscoverBaseUrl = "https://discover.search.hereapi.com";
41+
constexpr auto kDiscoverPath = "/v1/discover";
42+
43+
// Only the beginning of the response body is logged, the full document can
44+
// be large.
45+
constexpr size_t kMaxLoggedResponseSize = 512;
46+
47+
std::string ReadFile(const std::string& path) {
48+
std::ifstream stream(path, std::ios::in | std::ios::binary);
49+
if (!stream) {
50+
return {};
51+
}
52+
std::ostringstream contents;
53+
contents << stream.rdbuf();
54+
return contents.str();
55+
}
56+
57+
// Presents the client certificate during the mTLS handshake to retrieve a
58+
// bearer token. Returns an empty string in case of a failure.
59+
std::string RequestAccessToken(const std::string& cert_path,
60+
const std::string& key_path,
61+
const std::string& ca_path,
62+
const std::string& scope) {
63+
const auto client_cert_pem = ReadFile(cert_path);
64+
const auto client_key_pem = ReadFile(key_path);
65+
66+
if (client_cert_pem.empty() || client_key_pem.empty()) {
67+
OLP_SDK_LOG_ERROR_F(kLogTag,
68+
"Failed to read certificate or key file, cert='%s', "
69+
"key='%s'",
70+
cert_path.c_str(), key_path.c_str());
71+
return {};
72+
}
73+
74+
olp::authentication::MtlsSettings settings;
75+
settings.mtls_properties.client_cert_pem = client_cert_pem;
76+
settings.mtls_properties.client_key_pem = client_key_pem;
77+
if (!ca_path.empty()) {
78+
settings.mtls_properties.ca_cert_pem = ReadFile(ca_path);
79+
}
80+
if (!scope.empty()) {
81+
settings.mtls_properties.scope = scope;
82+
}
83+
84+
const olp::authentication::MtlsTokenProviderDefault token_provider(
85+
std::move(settings));
86+
87+
olp::client::CancellationContext context;
88+
const auto token_response = token_provider(context);
89+
90+
if (!token_response.IsSuccessful()) {
91+
OLP_SDK_LOG_ERROR_F(
92+
kLogTag, "mTLS sign in - Failure(%d): %s",
93+
static_cast<int>(token_response.GetError().GetErrorCode()),
94+
token_response.GetError().GetMessage().c_str());
95+
return {};
96+
}
97+
98+
OLP_SDK_LOG_INFO_F(kLogTag, "mTLS sign in - Success, expires in %lld s",
99+
static_cast<long long>(
100+
token_response.GetResult().GetExpiresIn().count()));
101+
102+
return token_response.GetResult().GetAccessToken();
103+
}
104+
105+
// Calls the HERE Discover Search API with the access token passed as the
106+
// bearer token.
107+
bool CallDiscoverApi(const std::string& access_token) {
108+
std::shared_ptr<olp::http::Network> http_client = olp::client::
109+
OlpClientSettingsFactory::CreateDefaultNetworkRequestHandler();
110+
111+
const std::multimap<std::string, std::string> query_params = {
112+
{"q", "döner"},
113+
{"at", "52.53083376480065,13.38469608732926"},
114+
{"limit", "3"}};
115+
116+
const auto url =
117+
olp::utils::Url::Construct(kDiscoverBaseUrl, kDiscoverPath, query_params);
118+
119+
auto request = olp::http::NetworkRequest(url)
120+
.WithVerb(olp::http::NetworkRequest::HttpVerb::GET)
121+
.WithHeader("Authorization", "Bearer " + access_token);
122+
123+
auto payload = std::make_shared<std::stringstream>();
124+
125+
std::promise<olp::http::NetworkResponse> promise;
126+
auto future = promise.get_future();
127+
128+
const auto outcome =
129+
http_client->Send(std::move(request), payload,
130+
[&promise](olp::http::NetworkResponse response) {
131+
promise.set_value(std::move(response));
132+
});
133+
134+
if (!outcome.IsSuccessful()) {
135+
OLP_SDK_LOG_ERROR_F(
136+
kLogTag, "Discover request was not sent - Failure: %s",
137+
olp::http::ErrorCodeToString(outcome.GetErrorCode()).c_str());
138+
return false;
139+
}
140+
141+
const auto response = future.get();
142+
143+
if (response.GetStatus() != olp::http::HttpStatusCode::OK) {
144+
OLP_SDK_LOG_ERROR_F(kLogTag, "Discover request - Failure(%d): %s",
145+
response.GetStatus(), response.GetError().c_str());
146+
return false;
147+
}
148+
149+
auto body = payload->str();
150+
OLP_SDK_LOG_INFO_F(kLogTag, "Discover request - Success, response: %s%s",
151+
body.substr(0, kMaxLoggedResponseSize).c_str(),
152+
body.size() > kMaxLoggedResponseSize ? "..." : "");
153+
154+
return true;
155+
}
156+
} // namespace
157+
158+
int RunExampleMtlsAuthentication(const std::string& cert_path,
159+
const std::string& key_path,
160+
const std::string& ca_path,
161+
const std::string& scope) {
162+
const auto access_token =
163+
RequestAccessToken(cert_path, key_path, ca_path, scope);
164+
if (access_token.empty()) {
165+
return -1;
166+
}
167+
168+
return CallDiscoverApi(access_token) ? 0 : -1;
169+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright (C) 2026 HERE Europe B.V.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* License-Filename: LICENSE
18+
*/
19+
20+
#pragma once
21+
22+
#include <string>
23+
24+
#include "Examples.h"
25+
26+
/**
27+
* @brief mTLS authentication example. Presents a client X.509 certificate
28+
* during the TLS handshake with the HERE mTLS token endpoint to retrieve a
29+
* bearer token, then calls the HERE Discover Search API with that token.
30+
* @param cert_path Path to the client certificate PEM file.
31+
* @param key_path Path to the client private key PEM file.
32+
* @param ca_path (Optional) Path to a CA certificate PEM file. Empty if not
33+
* used.
34+
* @param scope (Optional) The project HRN scope to request. Empty if not
35+
* used.
36+
* @return 0 if the token was retrieved and the Discover API call succeeded.
37+
*/
38+
EXAMPLES_API
39+
int RunExampleMtlsAuthentication(const std::string& cert_path,
40+
const std::string& key_path,
41+
const std::string& ca_path,
42+
const std::string& scope);

examples/Options.h

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2020-2021 HERE Europe B.V.
2+
* Copyright (C) 2020-2026 HERE Europe B.V.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -32,14 +32,35 @@ struct Option {
3232

3333
const Option kHelpOption{"-h", "--help", "Print the help message and exit."};
3434

35-
const Option kExampleOption{"-e", "--example",
36-
"Run example [=read|read_stream|write|cache]."};
35+
const Option kExampleOption{
36+
"-e", "--example",
37+
"Run example [=read|read_stream|write|cache|mtls-authentication]."};
3738

3839
const Option kKeyIdOption{"-i", "--key-id", "Here key ID to access OLP."};
3940

4041
const Option kKeySecretOption{"-s", "--key-secret",
4142
"Here secret key to access OLP."};
4243

44+
const Option kMtlsCertOption{
45+
"--cert", "--mtls-cert",
46+
"Path to the client certificate PEM file (required for the "
47+
"mtls-authentication example)."};
48+
49+
const Option kMtlsKeyOption{
50+
"--key", "--mtls-key",
51+
"Path to the client private key PEM file (required for the "
52+
"mtls-authentication example)."};
53+
54+
const Option kMtlsCaOption{
55+
"--ca", "--mtls-ca",
56+
"Path to a CA certificate PEM file (optional, used for the "
57+
"mtls-authentication example)."};
58+
59+
const Option kMtlsScopeOption{
60+
"--scope", "--mtls-scope",
61+
"Project HRN scope to request (optional, used for the "
62+
"mtls-authentication example)."};
63+
4364
const Option kCatalogOption{"-c", "--catalog",
4465
"Catalog HRN (HERE Resource Name)."};
4566

0 commit comments

Comments
 (0)