Here I have a function in which I'm sending a POST request which is used to save customer in the squareup with data and also set the headers with Authentication using variable_name.Header.Set()
But in body response it will always give me error of the:-
"errors":[
{"category":"AUTHENTICATION_ERROR",
"code":"UNAUTHORIZED",
"detail":"Your request did not include an `Authorization` http header with an access token. }]}
But In the function I'm setting the authentication token.
Code:-
func CreateCustomer(c *gin.Context) {
customer := models.Customer{}
bearer := strings.Split(c.Request.Header["Authorization"][0], "Bearer")// token pass in the postman.
bearerToken := strings.TrimSpace(bearer[1])
customerErr := json.NewDecoder(c.Request.Body).Decode(&customer)
if customerErr != nil {
fmt.Println(customerErr)
return
}
fmt.Println(customer)
bindData, err := json.Marshal(customer)
if err != nil {
panic(err)
}
var jsonStr = []byte(string(bindData))
url :="https://connect.squareup.com/v2/customers"
fmt.Println(url)
req, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr))
// I used this one too.
// req.Header.Set("Authorization", "Bearer "+bearerToken)
// req.Header.Set("Accept", "application/json")
req.Header.Add("Authorization", "Bearer "+bearerToken)
req.Header.Add("Accept", "application/json")
fmt.Println(req.Header)
if err != nil {
panic(err)
}
defer req.Body.Close()
body, _ := ioutil.ReadAll(req.Body)
fmt.Println("response Body:", string(body))
}
type Customer struct {
GivenName string `json:"given_name" bson:"given_name"`
FamilyName string `json:"family_name" bson:"family_name"`
CompanyName string `json:"company_name" bson:"company_name"`
Nickname string `json:"nickname" bson:"nickname"`
EmailAddress string `json:"email_address" bson:"email_address"`
Address Addresss `json:"address" bson:"address"`
PhoneNumber string `json:"phone_number" bson:"phone_number"`
ReferenceId string `json:"reference_id" bson:"reference_id"`
Note string `json:"note" bson:"note"`
}
The req.Header result is:-
map[X-Xss-Protection:[1; mode=block]
Keep-Alive:[timeout=60]
Accept:[application/json]
X-Permitted-Cross-Domain-Policies:[none]
Content-Type:[application/json]
Vary:[Origin, Accept-Encoding]
X-Content-Type-Options:[nosniff]
X-Download-Options:[noopen]
X-Frame-Options:[SAMEORIGIN]
Date:[Wed, 12 Dec 2018 03:41:16 GMT]
Strict-Transport-Security:[max-age=631152000]
Authorization:[Bearer YOUR_TOKEN HERE]]
Can anyone tell me that what error should I'm doing or where I do correction that it will able to save customer in the Squareup?
Your code sends POST request and after request is processed it adds headers to response struct:
response, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr))
You should set headers first and send request after that:
// create request
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
if err != nil {
panic(err)
}
// set headers
req.Header.Add("Authorization", "Bearer "+bearerToken)
req.Header.Add("Accept", "application/json")
// send request with headers
client := &http.Client{}
response, err := client.Do(req)
#Dmitry Harnitski this is the correct approach. Only reminder is that, try not confused between "authentication" and "authorization", which was kind of roadblock I encountered. It's not about literally meaning, everyone knows the difference. However, even in the syntax, a lot material used these two words interchangeably, which should not be encouraged, at least to my mind. The topic above is a good example: began with "authentication" as the question, then ended with "authorization" as the solution.
Related
I'm trying to connect the business central with the sharepoint, I already did it in the postman, it worked fine, the GET and POST.
But now Im trying to do it on BC, the token is created but not working, I think it has some problem with the connection or the URL's.
codeunit 50100 "APISharePoint"
{
var
OAuth2: Codeunit OAuth2;
ClientIdTxt: Label '4d148348-137a-40e7-b20c-a458c2a03c65', Locked = true;
ClientSecret: Label 'Ai2pOhE7yeZ8WRxrwSrHdpVtNqJg51tg2X+s8CJDLy4=', Locked = true;
ResourceUrlTxt: Label 'https://crmbc384959.sharepoint.com', Locked = true;
OAuthAuthorityUrlTxt: Label 'https://login.microsoftonline.com/ec36749a-af47-4f04-892c-98666b6cac1b/oauth2/v2.0/token', Locked = true;
procedure GetAccessToken(): Text
var
PromptInteraction: Enum "Prompt Interaction";
AccessToken: Text;
AuthCodeError: Text;
RedirectURLTxt: Text;
begin
// OAuth2.GetDefaultRedirectURL(RedirectURLTxt);
RedirectURLTxt := 'https://localhost';
if OAuth2.AcquireTokenWithClientCredentials(ClientIdTxt, ClientSecret, OAuthAuthorityUrlTxt, RedirectURLTxt, ResourceURLTxt, AccessToken) then
exit(AccessToken)
else
exit('');
end;
procedure HttpGet(AccessToken: Text; Url: Text /*var JResponse: JsonObject*/): Boolean
var
Client: HttpClient;
Headers: HttpHeaders;
RequestMessage: HttpRequestMessage;
ResponseMessage: HttpResponseMessage;
RequestContent: HttpContent;
ResponseText: Text;
IsSucces: Boolean;
begin
Headers := Client.DefaultRequestHeaders();
Headers.Add('Authorization', StrSubstNo('Bearer %1', AccessToken));
Headers.Add('Accept', 'application/json;odata=verbose');
RequestMessage.Content.GetHeaders(Headers); //asd
Headers.Remove('Content-Type');
Headers.Add('Content-Type', 'application/json;odata=verbose');
RequestMessage.SetRequestUri(Url);
RequestMessage.Method := 'GET';
if Client.Send(RequestMessage, ResponseMessage) then
if ResponseMessage.IsSuccessStatusCode() then begin
if ResponseMessage.Content.ReadAs(ResponseText) then
IsSucces := true;
// end else
// ResponseMessage.Content.ReadAs(ResponseText);
// JResponse.ReadFrom(ResponseText);
exit(IsSucces);
end;
end;
Maybe your problem is the combination of GET with a request content.
As far as i know, the underlaying implementation of the HttpClient in .NET does not support GET with a request content. You can read more about it here: How to use HttpClient to send content in body of GET request?
Maybe you can try GET without setting the content-type on the content or use POST or PUT instead.
But you didn't supply a error message so this is just a guess.
I am trying to authenticate to Azure service management / graph API using golang.
Using purely REST APIs.
No matter what I do, I always end up with error:
{"error":"invalid_request","error_description":"AADSTS900144: The request body must contain the following parameter: 'grant_type'.
Since I am not using SDK there is limited samples out there. Any help would be appreciated.
package main
import (
"bytes"
"encoding/json"
"io/ioutil"
"log"
"net/http"
)
func main() {
authendpoint := "https://login.microsoftonline.com/8xxxxx7-6372-4bcb-xxx-xxxxxx/oauth2/token"
jsonData := []byte(`{
"resource": "https://graph.microsoft.com",
"client_id": "xxxxxxxx-7549-4ea2-b00d-xxxxxxxxxxx",
"client_secret": "Q.xxxxxxxxxxxxxx-6_CgA4yOi_8sS-",
"grant_type": "client_credentials",
}`)
request, err := http.NewRequest("POST", authendpoint, bytes.NewBuffer(jsonData))
request.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(request)
if err != nil {
log.Fatal(err)
}
body, err := ioutil.ReadAll(resp.Body)
var res map[string]interface{}
json.NewDecoder(resp.Body).Decode(&res)
log.Println(string(body))
}
The Microsoft request docs posted by Praveen Premaratne show the request needs to be formatted using Content-Type: application/x-www-form-urlencoded which is a requirement for the OAuth 2.0 standard.
Here's the Microsoft docs and example:
https://learn.microsoft.com/en-us/graph/auth/auth-concepts#register-your-app-with-the-microsoft-identity-platform
POST /common/oauth2/v2.0/token HTTP/1.1
Host: https://login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&scope=user.read%20mail.read
&code=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq3n8b2JRLk4OxVXr...
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&grant_type=authorization_code
&client_secret=JqQX2PNo9bpM0uEihUPzyrh
Here's how to accomplish this:
package main
import (
"fmt"
"net/http"
"net/url"
"strings"
)
func main() {
authendpoint := "https://login.microsoftonline.com/8xxxxx7-6372-4bcb-xxx-xxxxxx/oauth2/token"
body := url.Values(map[string][]string{
"resource": {"https://graph.microsoft.com"},
"client_id": {"xxxxxxxx-7549-4ea2-b00d-xxxxxxxxxxx"},
"client_secret": {"Q.xxxxxxxxxxxxxx-6_CgA4yOi_8sS-"},
"grant_type": {"client_credentials"}})
request, err := http.NewRequest(
http.MethodPost,
authendpoint,
strings.NewReader(body.Encode()))
if err != nil {
panic(err)
}
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{}
resp, err := client.Do(request)
if err != nil {
panic(err)
}
fmt.Println(resp.StatusCode)
}
for me, it worked when I removed resource from the request body and added scope as new parameter in body.
I have a web application which sits behind Google's Identity Aware Proxy (IAP). IAP authenticates the user before forwarding to my web application. How can I access the already authenticated user from my web application?
In Getting the user's identity it states there are X-Goog-Authenticated-User-Email and X-Goog-Authenticated-User-Id headers. However, I don't see those in the response headers:
accept-ranges: bytes
alt-svc: clear
content-length: 14961
content-type: text/html; charset=utf-8
date: Thu, 01 Apr 2021 15:21:01 GMT
last-modified: Wed, 31 Mar 2021 19:34:58 GMT
via: 1.1 google
I do see a few cookies:
GCP_IAAP_AUTH_TOKEN_xxx
GCP_IAP_UID
GCP_IAP_XSRF_NONCE_xxx
For example, I want to be able to show their name and avatar photo in my web app to show that they are authenticated and logged in. I know that info is available via Google's OAuth2 struct, but how can I get that from IAP?
I was able to get this working after #JohnHanley mentioned that the headers only show up when running behind IAP. You cannot see them during local development.
I could see them after deploying a simple, temporary, /headers route which loops through them and writes to the ResponseWriter. X-Goog-Authenticated-User-Id, X-Goog-Authenticated-User-Email and X-Goog-Iap-Jwt-Assertion.
import (
"fmt"
"net/http"
"github.com/rs/zerolog/log"
)
func headersHandler(w http.ResponseWriter, r *http.Request) {
log.Info().Msg("Entering headersHandler")
fmt.Fprintf(w, "Request Headers\n\n")
log.Debug().Msg("Request Headers:")
for name, values := range r.Header {
log.Debug().Interface(name, values).Send()
fmt.Fprintf(w, "%s = %s\n", name, values)
}
}
This was a temporary route. Once I could confirm the headers, I deleted it.
Additionally, I had to enable Google's People API for the ProjectId where my web application was being hosted.
Afterwards, I did a test using the Go package for google.golang.org/api/people/v1 and found that the convention of using the currently authenticated user via people/me didn't work in my case since it returns the service account being used. Instead, I had to programmatically fill in the user id people/userid. Then it worked.
For my use-case, I created a /user route to return a subset of the user information, i.e. name, email, photo url.
Struct:
type GoogleUser struct {
Name string `json:"name"`
Email string `json:"email"`
PhotoUrl string `json:"photo_url"`
}
Handler:
func userHandler(w http.ResponseWriter, r *http.Request) {
log.Info().Msg("Entering userHandler")
var err error
// Make sure this is a valid API request
// Request header Content-Type: application/json must be present
if !ValidAPIRequest(r) {
err = writeJSONError(w, ResponseStatusNotFound("Not found"))
if err != nil {
log.Error().Msg(err.Error())
}
return
}
// Extract user id from header
var userId string = r.Header.Get("X-Goog-Authenticated-User-Id")
if userId != "" {
userId = strings.ReplaceAll(userId, "accounts.google.com:", "")
}
// Extract user email from header
var userEmail string = r.Header.Get("X-Goog-Authenticated-User-Email")
if userEmail != "" {
userEmail = strings.ReplaceAll(userEmail, "accounts.google.com:", "")
}
// Get the currently authenticated Google user
googleUser, err := GetCurrentGoogleUser(userId, userEmail)
if err != nil {
log.Error().Msg(err.Error())
err = writeJSONError(w, ResponseStatusInternalError(err.Error()))
if err != nil {
log.Error().Msg(err.Error())
}
return
}
// Write the JSON response
err = writeJSONGoogleUser(w, http.StatusOK, &googleUser)
if err != nil {
log.Error().Msg(err.Error())
}
}
Google People API:
func GetCurrentGoogleUser(userId string, userEmail string) (GoogleUser, error) {
// Pre-conditions
if userId == "" {
return GoogleUser{}, errors.New("userId is blank")
}
if userEmail == "" {
return GoogleUser{}, errors.New("userEmail is blank")
}
log.Debug().
Str("userId", userId).
Str("userEmail", userEmail).
Send()
ctx := context.Background()
// Instantiate a new People service
peopleService, err := people.NewService(ctx, option.WithAPIKey(GoogleAPIKey))
if err != nil {
return GoogleUser{}, err
}
// Define the resource name using the user id
var resourceName string = fmt.Sprintf("people/%s", userId)
// Get the user profile
profile, err := peopleService.People.Get(resourceName).PersonFields("names,photos").Do()
if err != nil {
return GoogleUser{}, err
}
log.Debug().
Interface("profile", profile).
Send()
return GoogleUser{Name: profile.Names[0].DisplayName, Email: userEmail, PhotoUrl: profile.Photos[0].Url}, nil
}
I am trying to do the modern authentication from azure ad with client certificate.
The process says.
Create an azure application.
Upload the certificate to azure application and get the thumbprint.
Generate the JWT token using that certificate(https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials)
Then use that jwt-token to get the access-token.
To get the access-token, call an API (https://login.microsoftonline.com//oauth2/token)
The HTTP method is of type POST and post body data will be x-www-form-urlencoded
After doing all the steps when i try to get the token.
I get this error.
{
"error": "invalid_client",
"error_description": "AADSTS700023: Client assertion audience claim does not match Realm issuer. Review the documentation at https://learn.microsoft.com/azure/active-directory/develop/active-directory-certificate-credentials .\r\nTrace ID: 714b5009-b74d-46b5-bd0e-3bea76272a01\r\nCorrelation ID: 2cb703bb-f325-44b5-a669-605bc7a81ac0\r\nTimestamp: 2020-08-28 07:31:19Z",
"error_codes": [
700023
],
"timestamp": "2020-08-28 07:31:19Z",
"trace_id": "714b5009-b74d-46b5-bd0e-3bea76272a01",
"correlation_id": "2cb703bb-f325-44b5-a669-605bc7a81ac0"
}
I am generating the JWT token using go code and parsing the PFX file.
func getAuthJWTToken() (string, error) {
clientID := "**********************"
_tenantName := "******************"
pfxFilePath := `E:\abcd.pfx`
certPassword := `*********`
authToken := ""
pfxFile, err := os.Open(pfxFilePath)
if err != nil {
return authToken, err
}
pfxfileinfo, _ := pfxFile.Stat()
var size int64 = pfxfileinfo.Size()
pfxbytes := make([]byte, size)
buffer := bufio.NewReader(pfxFile)
_, err = buffer.Read(pfxbytes)
//PFX to PEM for computation of signature
var pembytes []byte
blocks, err := pkcs12.ToPEM(pfxbytes, certPassword)
for _, b := range blocks {
pembytes = append(pembytes, pem.EncodeToMemory(b)...)
}
//Decoding the certificate contents from pfxbytes
pk, cert, err := pkcs12.Decode(pfxbytes, certPassword)
if cert == nil {
fmt.Printf("Bye")
return authToken, nil
}
if pk == nil {
}
pfxFile.Close() // close file
notToBeUsedBefore := time.Now()
expirationTime := time.Now().Add(3000 * time.Minute)
URL := fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/token", _tenantName)
id := guid.New()
claims := &claims{
StandardClaims: jwt.StandardClaims{
// In JWT, the expiry time is expressed as unix milliseconds
ExpiresAt: expirationTime.Unix(),
Audience: URL,
Issuer: clientID, // consumer key of the connected app, hardcoded
NotBefore: notToBeUsedBefore.Unix(),
Subject: clientID,
Id: id.String(),
},
}
//token_header map[string]interface{}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
sha1Fingerprint := sha1.Sum(cert.Raw)
var slice []byte
slice = sha1Fingerprint[:]
b64FingerPrint := base64.StdEncoding.EncodeToString([]byte(slice))
token.Header["x5t"] = b64FingerPrint
signKey, err := jwt.ParseRSAPrivateKeyFromPEM(pembytes) // parse the RSA key
tokenString, err := token.SignedString(signKey) // sign the claims with private key
fmt.Printf(fmt.Sprintf("JWT token is %s", tokenString))
return tokenString, err
}
I need the help to resolve this issue.
In sqaureup application Aplication_name in oauth option there is a redirect url Which will redirect a given url with the QueryString code. While I'm hitting https://connect.squareup.com/oauth2/authorize?client_id=YOUR_CLIENT_ID this url in the browser then it will redirect me to a given url in oauth with attached code. And then to take a access_token you have to give a POST request to given url https://connect.squareup.com/oauth2/token with the body
{
"client_id": "YOUR_APPLICATION_ID",
"client_secret": "YOUR_APPLICATION_SECRET",
"code": "M-Q7k-N0Emx_3cBqwbVLTQ",
"redirect_uri": "YOUR_REDIRECT_URI"
}
I do it same and send By method POST to this url with json data but it will gives me the error:-
{
"message": "Not Authorized",
"type": "service.not_authorized"
}
The Golang Code I'm using for this is :-
func Token(c *gin.Context) {
code := c.Query("code") // code be something like:-sq0cgp-wLVQt5HOLfug6xiVdmCDCf
splitCode := strings.Split(code, "-")
token := models.PostToken{
ClientID: "YOUR_APPLICATION_ID",
ClientSecret: "YOUR_APPLICATION_SECRET",
Code: splitCode[1],
RedirectUri: c.Request.Host + c.Request.URL.RequestURI(),
}
bindData, err := json.Marshal(token)
if err != nil {
panic(err)
}
var jsonStr = []byte(string(bindData))
url := "https://connect.squareup.com/oauth2/token"
req, err := http.Post(url, "application/json", bytes.NewBuffer(jsonStr))
fmt.Println(req, err)
}
Models struct:-
type PostToken struct {
ClientID string `json:"client_id" bson:"client_id"`
ClientSecret string `json:"client_secret" bson:"client_secret"`
Code string `json:"code" bson:"code"`
RedirectUri string `json:"redirect_uri" bson:"redirect_uri"`
}
You have to do some points I mentioned:-
First check your application Id.
In second parameter ClientSecret you have to use Oauth Application Secret key Which you will got from the application dashboard -> Oauth option.
In Code you don't have send the split code send simple string code value which your getting in the variable name code.
Fourth parameter is optional as the documentation says here.
Then you will got what you want :D.