Application Development Discussions
Join the discussions or start your own on all things application development, including tools and APIs, programming models, and keeping your skills sharp.
cancel
Showing results forΒ 
Search instead forΒ 
Did you mean:Β 

SAP Developer Challenge - APIs - Task 10 - Request an OAuth access token

qmacro
Employee
Employee

(Check out the SAP Developer Challenge - APIs blog post for everything you need to know about the challenge to which this task relates!)

This task brings you one more step closer to being able to call the API endpoint to look at the details of the directory you created in your SAP BTP account in Task 7, in that you'll learn how to request an access token to use as the credentials in the API call.

Background

In the Background section to Task 8, while you were in "reading mode", you looked at the SAP Help Portal documentation Account Administration Using APIs of the SAP Cloud Management Service. The first child node in this documentation that you come across is essential to this task. It's Getting an Access Token for SAP Cloud Management Service APIs and describes how to go about requesting an access token.

OAuth access tokens are obtained through various means, depending, to an extent, on what's being protected, who owns the resource, and so on. There are different so-called "grant types" in OAuth. They're sometimes also referred to as "flows". There's an archived CodeJam content project over on GitHub, in the SAP-archive organization, that gives a brief overview of these grant types. It's Exercise 02 - Understand OAuth 2.0 at a high level.

Being in the SAP-archive organization indicates that the content is no longer being maintained. But this particular exercise content is still valid and worthwhile reading.

OK, back to the SAP Help Portal documentation. You'll see that "the APIs of the SAP Cloud Management service for SAP BTP are protected with the OAuth 2.0 Password grant type". In some cases the Client Credentials grant type is at play, but not here in our context (mostly as we created the service instance in Cloud Foundry, for reasons explained in the corresponding task). So far so good.

And just to remind you of where you are in this group of tasks, you've now done steps 1 and 2 of the steps introduced in Task 8. This time you're tackling step 3.

  1. create an instance of the SAP Cloud Management Service, with a plan that contains the appropriate scope(s) that you need βœ…
  2. create a service key based on that instance βœ…
  3. use the details in the service key to request an access token
  4. use the access token thus obtained to authenticate a call to the API endpoint

So you'll need to follow the SAP Help Portal instructions to request an access token. Requesting an access token involves making an HTTP call to an Authorization Server endpoint. In making such a request, information must be supplied to specify and credentialize that request. The information required here, being a request for access to a resource that's protected with the Resource Owner Password Credentials grant type (you can see why this is shortened to just "Password" grant type, right?) is:

  • the type of grant type in play
  • the resource owner's username and password (this is the "Resource Owner" part of the grant type)
  • the client's identity (the identity of the client that will be making the API calls)

We know that:

  • the grant type in play is "password"
  • you are the owner of the resources that will be requested (the API facilities via the instance of the service you created, in your Cloud Foundry environment)
  • there's an ad hoc identity (client ID) and corresponding secret (client secret) generated that is used to represent the client that will be making the requests

The client (in the case of this group of tasks) will just be the HTTP client you use to make the calls. It could just as well be a script, a program, or another system.

You know where the resources are that you'll be requesting (the API endpoints), you know who the resource owner is (you yourself) but where is this client ID and client secret? Yes! They're in the service key data that you obtained in the previous task!

OK. To finish off this section, it's worth repeating what the section in the Understand OAuth 2.0 at a high level content says for this Resource Owner Password Credentials grant type:

This is a flow designed for use in the situation where there is strong trust between the Client and the Resource Owner - more specifically, when the Resource Owner trusts the Client (application) so much that they are willing to give their username & password credentials to the Client, which can then use them to request an access token. One redeeming feature of this grant type is that the Client does not have to store the credentials, as the access token granted can be long-lived, and / or the lifetime of the token can be extended by use of a refresh token.

OAuth as a concept is wonderful, but it does take some thinking time to let things sink in. Embrace the wonder of OAuth and its many facets, and enjoy this task!

Your task

Your task, then, is to request an access token in this context of the Resource Owner Password Credentials grant type with which the API endpoint(s) of the Accounts Service API are protected.

When your request is successful, you'll obtain not only an access token, but other data with it, in a JSON object. Here's what it will look like:

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "bearer...",
  "id_token": "eyJhbGciOiJSUzI1NiIs...",
  "refresh_token": "e72b61a9a9304dde963e...",
  "scope": "cis-central!b14.glob...",
  "jti": "579fea14a1cf47d7ab9e..."
}

Actually, there will also be another property, which has been deliberately removed from this sample. It's one that tells you when the access token supplied will expire, and the value is a duration, in seconds.

Identify this property, take the duration, in seconds, and compute the number of hours, rounding up to the nearest whole hour. That integer result is what you should send to the hash function and reply with, as usual, and as described in Task 0.

Hints and tips

The task here is, from one perspective, quite straightforward. But from another perspective, if this is your first time requesting an access token in an OAuth flow like this, it's worth taking your time and making sure you get the right values passed in the right way to the call to the Authorization Server, i.e. to the call to the resource that ends with /oauth/token.

You may have come across this URL path before; while it's not a standard, it's in common use to represent the endpoint of an OAuth Authorization Server that can be used to request an access token. It's shown, by the way, in the Getting an Access Token for SAP Cloud Management Service APIs documentation section in the SAP Help Portal.

Use the sample curl invocations in Step 3 of the Procedure section in Getting an Access Token for SAP Cloud Management Service APIs as a guide.

You can of course use whatever HTTP client you wish to make the request for the access token. Whether you use curl or something else, be aware that the data that you send, as described in the documentation, should be sent with content type application/x-www-form-urlencoded. This means a series of name and value pairs.

And take this as a clue - in particular the urlencoded part. While the example in the documentation shows explicitly that the HTTP POST method should be employed (with -x POST, but see the note below), you are likely to have some values that you need to transmit, that have characters that need to be so encoded. And for those of you lovely folks who are using curl, you may find the --data-urlencode parameter very useful! πŸ™‚

If you use curl and supply data with the -d parameter, then the HTTP POST method is used by default by curl, and you don't have to actually specify -X POST. Nor do you have to explicitly send a Content-Type header with the value application/x-www-form-urlencoded either, curl sends that by default.

For discussion

What approach did you take to request the access token? What form does the access token take? What other interesting information is returned in the JSON object?

80 REPLIES 80

ajmaradiaga
Employee
Employee

add84394f66dcde5ea17e5db7a6c9a653b46d5879d262956ad6b6b5f454eac3b

0 Kudos

I used the generate-password-grant-type script part of the btp CLI and APIs CodeJam - https://github.com/SAP-samples/cloud-btp-cli-api-codejam

0 Kudos

Haha, nice one @ajmaradiaga πŸ™‚

seVladimirs
Galactic 5
Galactic 5
0 Kudos

b4b7cee81c4a29e3d19a6dc01feac5f305bcd938a136f86ebe58fbb3c2b030ed

I've used insomnia.rest (not curl πŸ™ˆ) and JS to convert seconds to hours.

tobiasz_h
Galactic 3
Galactic 3
0 Kudos

12922f010f688276407f7a0735fabd1ce63730497445f0c79f6917700d12d1b0

harsh_itaverma
Galactic 4
Galactic 4
0 Kudos

15bccf5b4b8b57e7fa3416c71d948cefb7ed3b7f30fc5f9fa06152473738d512

0 Kudos

I used the CLI to request the token using CURL.

The JSON Object response made me curious to learn more on what's JTI and JWT.
 

Great! In fact, there is a task next week where we dive into some of those details πŸ˜‰

Nicolas
Galactic 3
Galactic 3
0 Kudos

b06bac4dc96d6378ea869c0136dfafc4ac89b076b01e0d6fc86c3904b361bb3f

kasch-code
Galactic 3
Galactic 3
0 Kudos

d1a2e1ed5596bf1b3d6e12ce0baf4e5da5b63cb19931e203a081f74a54fc4fcc

prachetas
Galactic 2
Galactic 2
0 Kudos

d30aabce8d7cf0b7b51c55026a32362d47bb816374ddf7b645866e2f015ae1c6

0 Kudos

Using  a combination of CF CLI/Postman/Integration Suite I have been doing these tasks. For JSON parsing groovy script.

It's so good to see different folks' different approaches here. Every one counts!

ceedee666
Galactic 3
Galactic 3
0 Kudos
f086beeae95a228ce3a6028bea6750f5fde9a6e98ce5050338092cb2a1469df6

I used Python and the requests library to get the token. Took me a while to figure out that I don't need to URL-encode the client id and client secret as this is handled automatically by the library.

https://github.com/ceedee666/sap-dev-challenge-apis-in-python/blob/main/sap-dev-challange-apis.ipynb

se38
Galactic 4
Galactic 4
0 Kudos

1c60fb87fc4ed8f8c6fc033563a5436c6c3d4dccfd0d89daa644e056da1a0dd3

Postman

dan_wroblewski
Employee
Employee
0 Kudos

776ef764f16ac78ef908a5437bdb97c0829951eee7eb60c9ac7b6f9cde60a297




--------------
See all my blogs and connect with me on Twitter / LinkedIn

dan_wroblewski
Employee
Employee

I had trouble running curl (I use in BAS) because of the client ID and secret had special characters that BASH did not like, and if I escaped them then I couldn't authenticate. How could I run the curl command?

Also, it seemed if I ran cf curl vs curl they ran differently. Can you explain what's the difference?




--------------
See all my blogs and connect with me on Twitter / LinkedIn

Hi @dan_wroblewski, similar experience I had when using curl in BAS. The client id and secret kept giving me error. I kept getting error on the command options too like

"bash: -H: command not found
bash: !b262513: event not found
bash: -d: command not found". 

I tried this command and it worked fine.
Let me know your comments on this.

curl --request POST \
--url 'https://<url>/oauth/token' \
--header 'Accept: application/json' \
--data 'client_id=<clientid>' \
--data 'client_secret=<clientsecret>' \
--data 'grant_type=password' \
--data 'username=<email>' \
--data 'password=<password>' \
--data 'response_type=token'


Wow, that worked really nicely ... thanks so much!!

P.S.: The 2nd to last --data is missing the \ at the end of the line (but the command still worked fine) 




--------------
See all my blogs and connect with me on Twitter / LinkedIn

0 Kudos

Oh yes, my bad; just edited the comment.

Hi, I just want to share that it works fine for me as below picture. 

bas-curl-get-access-token.png

btw, this is my CAP challenge repo in BAS πŸ˜πŸ˜

I got the same result both in my local machine and in BAS.

From Sample Code for Mac OS (Password grant type): in the document

curl -L -X POST '<uaa_url>/oauth/token' \ 
-H 'Content-Type: application/x-www-form-urlencoded' \ 
-u '<clientid>:<clientsecret>' \ 
-d 'grant_type=password' \ 
-d 'username=<user email>' \
-d 'password=<password>'

for those special chars in clientid and clientsecret, it should work because we already wrap them inside the pair of single quote.

I have change just in part of username and password, I use --data-urlencode instead of -d because I have special character in my password too.

hope this help

Wises

Great stuff @bztoy , yes, single quotes FTW (I've just written about it here in fact https://qmacro.org/blog/posts/2023/08/25/bash-shell-expansion-inside-double-quotes/) and yes, --data-urlencode is a great feature of curl! πŸ‘

Well done @harsh_itaverma for helping @dan_wroblewski with the invocation. 

Daniel - the reason you had trouble is because ! is a special character in Bash which invokes "history expansion", i.e. you can refer to previous commands in your shell's history by preceding the command's number with a !).

I've written up a quick blog post on this, and also on shell parameter expansion, and how the difference between double and single quotes makes all the difference.

πŸ‘‰ Bash shell expansion inside quotes

Hope this helps!

 

Tomas_Buryanek
Galactic 4
Galactic 4
0 Kudos

I am trying CURL from BAS, but I am getting  CSRF error β˜ΉοΈ

Could not verify the provided CSRF token because no token was found to compare

EDIT: /oath/ instead of /oauth/ πŸ€¦β€(facepalm)

Hi @Tomas_Buryanek

I could be wrong here, but did you try the command cf login. 
I got the same error and this worked for me. 

0 Kudos

Thank you! But it happens even after succesful "cf login" 😞

You'll need to share a bit more context here ... what was the command, what was the entire output, etc πŸ™‚

0 Kudos

Thank you! Solved now. All my commands were correct. But just one typo mistake:

/oath/ instead of /oauth/ πŸ€¦β€

bztoy
Galactic 4
Galactic 4
0 Kudos

6acc7e926a4117cac8a69c5c7f23fa53d3a1aee05f54870a1e7d570c88d50767

What approach did you take to request the access token?


I write a bash script using jq and curl to do the job. it is the same thing that I learned from the OAuth SAP dev hands-on session πŸ˜‚

jq helps to read uaa.url, uaa.clientid and clientsecret in raw string mode.

then use information above + the credentials(user+password) make a post call with curl in the same format as provided in the document.

What form does the access token take?


The token type is "bearer"

 

What other interesting information is returned in the JSON object?


IMO, it would be property "jti"

0 Kudos

Great replies, thank you!

thomas_jung
Employee
Employee
0 Kudos

20bb9f46c29b84bb0fe4b9e7eaf490200c27646489ed09ce057cf62a1a6e77d3

shotokka
Galactic 3
Galactic 3
0 Kudos

7205b6ae59a595c0b24cab5c731f0f8552bf5f2e41df854a3bce265e6d3e6f24

PriyankaChak
Galactic 3
Galactic 3
0 Kudos

3edd6e5909f8d29060b81751a2fa8088d8dcdda07eaccd88f78a357817e163a6

geek
Galactic 3
Galactic 3
0 Kudos

0ac454aceebf36ae1b7d9e357338ba93b0f7f8038e5987617275db285ba601b6

anupamduttaroy
Galactic 4
Galactic 4
0 Kudos

31979ef38c6c4bfde0cf837114bb071fcffbb50469f8cd387cbf0b1c7909b7b9