Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ set(OLP_SDK_DATASERVICE_READ_EXAMPLE_TARGET dataservice-read-example)
set(OLP_SDK_DATASERVICE_WRITE_EXAMPLE_TARGET dataservice-write-example)
set(OLP_SDK_DATASERVICE_CACHE_EXAMPLE_TARGET dataservice-cache-example)
set(OLP_SDK_DATASERVICE_READ_STREAM_LAYER_EXAMPLE_TARGET dataservice-read-stream-layer-example)
set(OLP_SDK_MTLS_AUTHENTICATION_EXAMPLE_TARGET mtls-authentication-example)

set(OLP_SDK_EXAMPLE_SUCCESS_STRING "Example has finished successfully")
set(OLP_SDK_EXAMPLE_FAILURE_STRING "Example failed!")
Expand Down Expand Up @@ -81,6 +82,14 @@ else()
olp-cpp-sdk-authentication
olp-cpp-sdk-dataservice-read)

add_library(${OLP_SDK_MTLS_AUTHENTICATION_EXAMPLE_TARGET}
./MtlsAuthenticationExample.cpp
./Examples.h
./MtlsAuthenticationExample.h)

target_link_libraries(${OLP_SDK_MTLS_AUTHENTICATION_EXAMPLE_TARGET}
olp-cpp-sdk-authentication)

target_compile_definitions(${OLP_SDK_DATASERVICE_READ_EXAMPLE_TARGET}
PRIVATE EXAMPLES_LIBRARY)
target_compile_definitions(${OLP_SDK_DATASERVICE_WRITE_EXAMPLE_TARGET}
Expand All @@ -89,6 +98,8 @@ else()
PRIVATE EXAMPLES_LIBRARY)
target_compile_definitions(${OLP_SDK_DATASERVICE_READ_STREAM_LAYER_EXAMPLE_TARGET}
PRIVATE EXAMPLES_LIBRARY)
target_compile_definitions(${OLP_SDK_MTLS_AUTHENTICATION_EXAMPLE_TARGET}
PRIVATE EXAMPLES_LIBRARY)
if(BUILD_SHARED_LIBS)
target_compile_definitions(${OLP_SDK_DATASERVICE_READ_EXAMPLE_TARGET}
PUBLIC EXAMPLES_SHARED_LIBRARY)
Expand All @@ -98,6 +109,8 @@ else()
PUBLIC EXAMPLES_SHARED_LIBRARY)
target_compile_definitions(${OLP_SDK_DATASERVICE_READ_STREAM_LAYER_EXAMPLE_TARGET}
PUBLIC EXAMPLES_SHARED_LIBRARY)
target_compile_definitions(${OLP_SDK_MTLS_AUTHENTICATION_EXAMPLE_TARGET}
PUBLIC EXAMPLES_SHARED_LIBRARY)
endif()

add_executable(${OLP_SDK_DATASERVICE_EXAMPLE_TARGET}
Expand All @@ -109,6 +122,7 @@ else()
${OLP_SDK_DATASERVICE_READ_EXAMPLE_TARGET}
${OLP_SDK_DATASERVICE_WRITE_EXAMPLE_TARGET}
${OLP_SDK_DATASERVICE_CACHE_EXAMPLE_TARGET}
${OLP_SDK_DATASERVICE_READ_STREAM_LAYER_EXAMPLE_TARGET})
${OLP_SDK_DATASERVICE_READ_STREAM_LAYER_EXAMPLE_TARGET}
${OLP_SDK_MTLS_AUTHENTICATION_EXAMPLE_TARGET})

endif()
169 changes: 169 additions & 0 deletions examples/MtlsAuthenticationExample.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* Copyright (C) 2026 HERE Europe B.V.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* License-Filename: LICENSE
*/

#include "MtlsAuthenticationExample.h"

#include <olp/authentication/MtlsTokenProvider.h>
#include <olp/core/client/OlpClientSettingsFactory.h>
#include <olp/core/http/HttpStatusCode.h>
#include <olp/core/http/Network.h>
#include <olp/core/logging/Log.h>
#include <olp/core/utils/Url.h>

#include <fstream>
#include <future>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <utility>

namespace {
constexpr auto kLogTag = "mtls-authentication-example";

constexpr auto kDiscoverBaseUrl = "https://discover.search.hereapi.com";
constexpr auto kDiscoverPath = "/v1/discover";

// Only the beginning of the response body is logged, the full document can
// be large.
constexpr size_t kMaxLoggedResponseSize = 512;

std::string ReadFile(const std::string& path) {
std::ifstream stream(path, std::ios::in | std::ios::binary);
if (!stream) {
return {};
}
std::ostringstream contents;
contents << stream.rdbuf();
return contents.str();
}

// Presents the client certificate during the mTLS handshake to retrieve a
// bearer token. Returns an empty string in case of a failure.
std::string RequestAccessToken(const std::string& cert_path,
const std::string& key_path,
const std::string& ca_path,
const std::string& scope) {
const auto client_cert_pem = ReadFile(cert_path);
const auto client_key_pem = ReadFile(key_path);

if (client_cert_pem.empty() || client_key_pem.empty()) {
OLP_SDK_LOG_ERROR_F(kLogTag,
"Failed to read certificate or key file, cert='%s', "
"key='%s'",
cert_path.c_str(), key_path.c_str());
return {};
}

olp::authentication::MtlsSettings settings;
settings.mtls_properties.client_cert_pem = client_cert_pem;
settings.mtls_properties.client_key_pem = client_key_pem;
if (!ca_path.empty()) {
settings.mtls_properties.ca_cert_pem = ReadFile(ca_path);
}
if (!scope.empty()) {
settings.mtls_properties.scope = scope;
}

const olp::authentication::MtlsTokenProviderDefault token_provider(
std::move(settings));

olp::client::CancellationContext context;
const auto token_response = token_provider(context);

if (!token_response.IsSuccessful()) {
OLP_SDK_LOG_ERROR_F(
kLogTag, "mTLS sign in - Failure(%d): %s",
static_cast<int>(token_response.GetError().GetErrorCode()),
token_response.GetError().GetMessage().c_str());
return {};
}

OLP_SDK_LOG_INFO_F(kLogTag, "mTLS sign in - Success, expires in %lld s",
static_cast<long long>(
token_response.GetResult().GetExpiresIn().count()));

return token_response.GetResult().GetAccessToken();
}

// Calls the HERE Discover Search API with the access token passed as the
// bearer token.
bool CallDiscoverApi(const std::string& access_token) {
std::shared_ptr<olp::http::Network> http_client = olp::client::
OlpClientSettingsFactory::CreateDefaultNetworkRequestHandler();

const std::multimap<std::string, std::string> query_params = {
{"q", "döner"},
{"at", "52.53083376480065,13.38469608732926"},
{"limit", "3"}};

const auto url =
olp::utils::Url::Construct(kDiscoverBaseUrl, kDiscoverPath, query_params);

auto request = olp::http::NetworkRequest(url)
.WithVerb(olp::http::NetworkRequest::HttpVerb::GET)
.WithHeader("Authorization", "Bearer " + access_token);

auto payload = std::make_shared<std::stringstream>();

std::promise<olp::http::NetworkResponse> promise;
auto future = promise.get_future();

const auto outcome =
http_client->Send(std::move(request), payload,
[&promise](olp::http::NetworkResponse response) {
promise.set_value(std::move(response));
});

if (!outcome.IsSuccessful()) {
OLP_SDK_LOG_ERROR_F(
kLogTag, "Discover request was not sent - Failure: %s",
olp::http::ErrorCodeToString(outcome.GetErrorCode()).c_str());
return false;
}

const auto response = future.get();

if (response.GetStatus() != olp::http::HttpStatusCode::OK) {
OLP_SDK_LOG_ERROR_F(kLogTag, "Discover request - Failure(%d): %s",
response.GetStatus(), response.GetError().c_str());
return false;
}

auto body = payload->str();
OLP_SDK_LOG_INFO_F(kLogTag, "Discover request - Success, response: %s%s",
body.substr(0, kMaxLoggedResponseSize).c_str(),
body.size() > kMaxLoggedResponseSize ? "..." : "");

return true;
}
} // namespace

int RunExampleMtlsAuthentication(const std::string& cert_path,
const std::string& key_path,
const std::string& ca_path,
const std::string& scope) {
const auto access_token =
RequestAccessToken(cert_path, key_path, ca_path, scope);
if (access_token.empty()) {
return -1;
}

return CallDiscoverApi(access_token) ? 0 : -1;
}
42 changes: 42 additions & 0 deletions examples/MtlsAuthenticationExample.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2026 HERE Europe B.V.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* License-Filename: LICENSE
*/

#pragma once

#include <string>

#include "Examples.h"

/**
* @brief mTLS authentication example. Presents a client X.509 certificate
* during the TLS handshake with the HERE mTLS token endpoint to retrieve a
* bearer token, then calls the HERE Discover Search API with that token.
* @param cert_path Path to the client certificate PEM file.
* @param key_path Path to the client private key PEM file.
* @param ca_path (Optional) Path to a CA certificate PEM file. Empty if not
* used.
* @param scope (Optional) The project HRN scope to request. Empty if not
* used.
* @return 0 if the token was retrieved and the Discover API call succeeded.
*/
EXAMPLES_API
int RunExampleMtlsAuthentication(const std::string& cert_path,
const std::string& key_path,
const std::string& ca_path,
const std::string& scope);
27 changes: 24 additions & 3 deletions examples/Options.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2021 HERE Europe B.V.
* Copyright (C) 2020-2026 HERE Europe B.V.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -32,14 +32,35 @@ struct Option {

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

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

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

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

const Option kMtlsCertOption{
"--cert", "--mtls-cert",
"Path to the client certificate PEM file (required for the "
"mtls-authentication example)."};

const Option kMtlsKeyOption{
"--key", "--mtls-key",
"Path to the client private key PEM file (required for the "
"mtls-authentication example)."};

const Option kMtlsCaOption{
"--ca", "--mtls-ca",
"Path to a CA certificate PEM file (optional, used for the "
"mtls-authentication example)."};

const Option kMtlsScopeOption{
"--scope", "--mtls-scope",
"Project HRN scope to request (optional, used for the "
"mtls-authentication example)."};

const Option kCatalogOption{"-c", "--catalog",
"Catalog HRN (HERE Resource Name)."};

Expand Down
Loading
Loading