SQL Server
SQL Server
Implementing DPoP with OAuth2 Client Credentials
See more AI Examples
Demonstrating Proof-of-Possession (DPoP) is an OAuth 2.0 extension (RFC 9449) that secures access tokens by binding them to a specific client’s private key, preventing stolen token misuse. It requires the client to sign a JWT (DPoP Proof) with every request, proving they hold the private key.This example shows how to use Chilkat v11.4.0 or later to automatically add the following headers to HTTP requests:
Authorization: DPoP <access_token> DPoP: <new JWT signed with same key>
Chilkat SQL Server Downloads
-- Important: See this note about string length limitations for strings returned by sp_OAMethod calls.
--
CREATE PROCEDURE ChilkatSample
AS
BEGIN
DECLARE @hr int
DECLARE @iTmp0 int
-- Important: Do not use nvarchar(max). See the warning about using nvarchar(max).
DECLARE @sTmp0 nvarchar(4000)
DECLARE @success int
SELECT @success = 0
DECLARE @privKey int
EXEC @hr = sp_OACreate 'Chilkat.PrivateKey', @privKey OUT
IF @hr <> 0
BEGIN
PRINT 'Failed to create ActiveX component'
RETURN
END
-- Load your ECDSA private key. (Keys uses with DPoP must always be ECDSA keys. Not RSA.)
EXEC sp_OAMethod @privKey, 'LoadAnyFormatFile', @success OUT, 'qa_data/pem/ecc_privKey.pem', ''
IF @success = 0
BEGIN
EXEC sp_OAGetProperty @privKey, 'LastErrorText', @sTmp0 OUT
PRINT @sTmp0
EXEC @hr = sp_OADestroy @privKey
RETURN
END
DECLARE @tokenEndpoint nvarchar(4000)
SELECT @tokenEndpoint = 'https://demo.duendesoftware.com/connect/token'
DECLARE @apiEndpoint nvarchar(4000)
SELECT @apiEndpoint = 'https://demo.duendesoftware.com/api/dpop/test'
-- Provide the details needed for Chilkat to automatically fetch the access token
-- using the client_credentials OAuth2 flow.
DECLARE @json int
EXEC @hr = sp_OACreate 'Chilkat.JsonObject', @json OUT
EXEC sp_OAMethod @json, 'UpdateString', @success OUT, 'client_secret', 'secret'
EXEC sp_OAMethod @json, 'UpdateString', @success OUT, 'client_id', 'm2m.dpop'
EXEC sp_OAMethod @json, 'UpdateString', @success OUT, 'token_endpoint', @tokenEndpoint
EXEC sp_OAMethod @json, 'UpdateString', @success OUT, 'scope', 'api'
DECLARE @http int
EXEC @hr = sp_OACreate 'Chilkat.Http', @http OUT
EXEC sp_OASetProperty @http, 'SessionLogFilename', 'c:/aaworkarea/sessionLog.txt'
-- Provide private key to be used with DPoP
EXEC sp_OAMethod @http, 'SetDPoPKey', @success OUT, @privKey
IF @success = 0
BEGIN
EXEC sp_OAGetProperty @http, 'LastErrorText', @sTmp0 OUT
PRINT @sTmp0
EXEC @hr = sp_OADestroy @privKey
EXEC @hr = sp_OADestroy @json
EXEC @hr = sp_OADestroy @http
RETURN
END
EXEC sp_OAMethod @json, 'Emit', @sTmp0 OUT
EXEC sp_OASetProperty @http, 'AuthToken', @sTmp0
-- When we make the first request to an API endpoint, we don't yet have
-- an OAuth2 access token. Chilkat (internally) will use the token endpoint
-- to automatically get an access token.
-- Chilkat will then add the following headers to the request sent to the API endpoint:
-- Authorization: DPoP <access_token>
-- DPoP: <new JWT signed with same key>
-- For subsequent requests using the same Chilkat HTTP object instance,
-- the same access token will be used with a newly generated DPoP header for each request.
-- When the access_token is expired or about to expire, Chilkat will automatically (internally)
-- refresh the access token. Your application does not need to be concerned with the
-- complexities of fetching the access token or managing the refresh, or generating
-- the DPoP header for each request.
DECLARE @resp nvarchar(4000)
EXEC sp_OAMethod @http, 'QuickGetStr', @resp OUT, @apiEndpoint
EXEC sp_OAGetProperty @http, 'LastMethodSuccess', @iTmp0 OUT
IF @iTmp0 = 0
BEGIN
EXEC sp_OAGetProperty @http, 'LastErrorText', @sTmp0 OUT
PRINT @sTmp0
EXEC @hr = sp_OADestroy @privKey
EXEC @hr = sp_OADestroy @json
EXEC @hr = sp_OADestroy @http
RETURN
END
DECLARE @statusCode int
EXEC sp_OAGetProperty @http, 'LastStatus', @statusCode OUT
PRINT 'response status code = ' + @statusCode
PRINT @resp
-- Here is a Session log showing the raw HTTP request(s) and response(s).
-- Notice how Chilkat automatically fetched the access_token and then used it in the API request.
-- ---- Sending Mon, 30 Mar 2026 20:09:44 GMT ----
-- POST /connect/token HTTP/1.1
-- Host: demo.duendesoftware.com
-- Content-Type: application/x-www-form-urlencoded
-- Content-Length: 79
-- DPoP: ****
--
-- client_secret=secret&client_id=m2m.dpop&scope=api&grant_type=client_credentials
-- ---- Received Mon, 30 Mar 2026 20:09:44 GMT ----
-- HTTP/1.1 200 OK
-- Date: Mon, 30 Mar 2026 20:09:43 GMT
-- Content-Type: application/json; charset=UTF-8
-- Transfer-Encoding: chunked
-- Connection: keep-alive
-- Server: Kestrel
-- Cache-Control: no-store, no-cache, max-age=0
-- Pragma: no-cache
-- X-REVISION: 4f42f7e7
--
-- 374
-- {"access_token":"eyJhbGciOiJSUzI1NiIsImtpZCI6IjE1OEUyRDQ1NzhFNDMyOUJBMzRGNzkxQTQ4Nzk5NzYwIiwieDV0IjoiczkyM2RnYnhWbnhoaTJSZmR1cWZyZXZIYUUwIi
-- widHlwIjoiYXQrand0In0.eyJpc3MiOiJodHRwczovL2RlbW8uZHVlbmRlc29mdHdhcmUuY29tIiwibmJmIjoxNzc0OTAxMzgzLCJpYXQiOjE3NzQ5MDEzODMsImV4cCI6MTc3NDkwN
-- Dk4MywiYXVkIjoiYXBpIiwiY25mIjp7ImprdCI6IlVMR2J4ZXdNOEZUNDhTd3hKeUY3YnJuMDZWMDdPZ3VpdW41d1ZzSVVENEUifSwic2NvcGUiOlsiYXBpIl0sImNsaWVudF9pZCI6
-- Im0ybS5kcG9wIiwianRpIjoiQ0Q5MjBFQzQyOTRBOTA3QUUyRjMyQjlCREQzQTA3QzMifQ.bbq8tNMVtJp79Wc9xrAsE0h8oOfdxEKtGZ3qHpJX-mvSS4FVXLoO2bfpq1nhFryfbZkk
-- ENfoIwSQjb_35aziq2NrnpWwQ4vDF1b9K-b7eJXPmJybm61DAP9HSE3ZI0jMrMTRFLtjwvE4WV6ZXNTwN0Pzn6teog1i09PZQkaGtlJEBjsBaN5N7OsQvtgFLD0sNkjy1JTw5LMNE7u
-- 5-W32qv-XDk9KEGfoBkqyt-SR_A2wI7oZvl4OGnYyX8tUOFszlHZKy0_0V3Oc9wkHRM2JZDA_LfTqldJHR5CF5FuNiJ2hp-kioUA-i0gWxhqnVKVspJqQAN3Z95RDl9jSf-pYSg",
-- "expires_in":3600,"token_type":"DPoP","scope":"api"}
-- 0
--
--
-- ---- Sending Mon, 30 Mar 2026 20:09:44 GMT ----
-- GET /api/dpop/test HTTP/1.1
-- Host: demo.duendesoftware.com
-- Accept: */*
-- Accept-Encoding: gzip
-- Authorization: ****
-- DPoP: ****
--
--
-- ---- Received Mon, 30 Mar 2026 20:09:44 GMT ----
-- HTTP/1.1 200 OK
-- Date: Mon, 30 Mar 2026 20:09:44 GMT
-- Content-Type: application/json; charset=utf-8
-- Transfer-Encoding: chunked
-- Connection: keep-alive
-- Server: Kestrel
-- X-REVISION: 4f42f7e7
--
-- 420
-- [{"type":"iss","value":"https://demo.duendesoftware.com"},{"type":"nbf","value":"1774901383"},{"type":"iat","value":"1774901383"},
-- {"type":"exp","value":"1774904983"},{"type":"aud","value":"api"},{"type":"cnf","value":"{\u0022jkt\u0022:\u0022ULGbxewM8FT48SwxJyF7brn06V07Oguiun5wVsIUD4E\u0022}"},
-- {"type":"scope","value":"api"},{"type":"client_id","value":"m2m.dpop"},{"type":"jti","value":"CD920EC4294A907AE2F32B9BDD3A07C3"},
-- {"type":"authorization_scheme","value":"DPoP"},{"type":"proof_token","value":"eyJhbGciOiJFU .... aGrGOgkRXLQ"}]
-- 0
--
--
EXEC @hr = sp_OADestroy @privKey
EXEC @hr = sp_OADestroy @json
EXEC @hr = sp_OADestroy @http
END
GO