Golang. How to automatically authorize my app to access spotify APIs? - http

I've registered on Spotify my app and I obtained my credentials.
I'm using Spotify web api through zmb3/spotify Go wrapper.
const redirectURI = "http://localhost:8080/auth_callback" //Already registered
const CLIENT_ID = "myId"
const CLIENT_SECRET = "mySecret"
const STATE = "myState"
I have a local server (written in Golang) listening for requests on some routes. When I define routes, I create the Spotify Authenticator with desired scopes and I obtain the url.
If I print the authorize url to stdout and I click it (or also with copy and paste) redirectURI is correctly called!
Here is the code...
var auth spotify.Authenticator
var token *oauth2.Token
var client spotify.Client
var authorize_url string
func main(){
fmt.Println("[MAIN] Starting Server")
auth = spotify.NewAuthenticator(redirectURI, spotify.ScopeUserReadPrivate, spotify.ScopeUserTopRead)
auth.SetAuthInfo(CLIENT_ID, CLIENT_SECRET)
authorize_url = auth.AuthURL(STATE)
var wg sync.WaitGroup
router.HandleFunc("/authorize_spotify", authHandler)
router.HandleFunc("/auth_callback", completeAuth)
http.Handle("/", router)
wg.Add(1)
go start(&wg, 0)
defer wg.Wait()
fmt.Println("[MAIN] Running Server... " + url)
}
//SPOTIFY API MANAGEMENT
func completeAuth(w http.ResponseWriter, r *http.Request) {
fmt.Println("Authentication Redirect Received")
token, err := auth.Token(STATE, r)
if err != nil {
http.Error(w, "Couldn't get token", http.StatusForbidden)
log.Fatal(err)
}
if st := r.FormValue("state"); st != STATE {
http.NotFound(w, r)
log.Fatalf("State mismatch: %s != %s\n", st, STATE)
}
// use the token to get an authenticated client
println(token)
client = auth.NewClient(token)
}
Now I want to make /authorize_spotify automatically call the obtained url.
I wrote these two lines.
func authHandler(w http.ResponseWriter, r *http.Request){
fmt.Println("[AUTH - HANDLER]")
http.Redirect(w, r, authorize_url, 302)
}
However, when I contact localhost:8080/authorize_spotify
authHandler is NEVER Called (no output on stdout, despite the fmt.Println(...)).
Misteriously, I'm redirected to the redirectURI at the same way, but it fails.
Seeing network http exchanges I noted that the authorize_url that I printed is a bit different from the authorize request misteriously sent (in the latter the user-top-read scope is missing), that confirms that the authHandler is not called.
How can I achieve my token calling authorize_url from code?
Thanks in advance!

Related

How to validate the firebase jwt token in my google cloud function?

Users are authenticated in the frontend via
const googleAuthProvider = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithPopup(googleAuthProvider);
after that I am calling a cloud function with the firebase callable:
const nodeStateCall = functions.httpsCallable('myFunction');
nodeStateCall().then(...);
This workks without any problems and I recieve the jwt token in my go function.
I tried many different variations to authenticate this token. The function which I am using now is idtoken.Validate() from the google.golang.org/api/idtoken pacakge.
My function:
func verifyIdToken(idToken string)(tokenInfo *idtoken.Payload, err error) {
log.Print("running verify Token")
splitToken := strings.Split(idToken, "Bearer ")[1]
log.Print("split Token:"+splitToken)
tokenInfo, err = idtoken.Validate(context.Background(),splitToken,"MYAUDIENCE")
if err != nil {
log.Print("error from tokenInfoCall.Do()")
log.Print(err)
return nil, err
}
log.Print("Finished verify token.")
return tokenInfo, nil
}
but I keep getting "invalid value" when I send the token.
I also tried the oauth2 service:
oauth2Service, err := oauth2.NewService(context.Background())
tokenInfoCall := oauth2Service.Tokeninfo()
tokenInfoCall.IdToken(splitToken)
tokenInfo, err := tokenInfoCall.Do()
which didn't work either.
What do I have to do to validate the firebase jwt token in my go function?
Because you are using firebase service, you need to use this to verify your token:
https://firebase.google.com/docs/auth/admin/verify-id-tokens#verify_id_tokens_using_the_firebase_admin_sdk
Note that you should setup your function in the same project so the library will query correctly.

Force Gitlab to retry webhooks on failure with Go

I want to watch for every events in a Gitlab project and store them in an external service. For this, I use Gitlab Webhooks. I made a little local HTTP server in Go that listens for Gitlab's POSTs and forward them to an external service. Hooks contains every information I needed so it seems that this architecture is fine:
Gitlab > HTTPServer > External Service.
My problem is when the external service is down, I cannot manage to make Gitlab retry the failed requests. As the documentation says:
GitLab ignores the HTTP status code returned by your endpoint.
Your endpoint should ALWAYS return a valid HTTP response. If you do not do this then GitLab will think the hook failed and retry it.
It is very surprising that Gitlab does not have a proper way to ask for a webhook retry. I have to explicitly return an invalid http response. Moreover, I cannot find an API endpoint to list all failed webhooks and ask for resend.
Question: How to explicitly return an invalid HTTP response with the standard "net/http" library in order to force Gitlab to retry Webhooks?
As written in the comments, a webhook is a mere notification that an event occurred, and potentially some data is sent, typically as JSON data.
It is your responsibility to persist the event itself and the data you want/need to process that was sent with it. Below you will find a commented example. Note that this does not include incremental backoffs, but that should be easy to add:
package main
import (
"encoding/json"
"flag"
"io"
"log"
"net/http"
"os"
"path/filepath"
"github.com/joncrlsn/dque"
)
var (
bind string
queueDir string
segmentSize int
)
// You might want to add request headers and stuff
type webhookContent struct {
Foo string
Bar int
}
func init() {
flag.StringVar(&bind, "bind", ":8080", "The address to bind to")
flag.StringVar(&queueDir, "path", "./queue", "path to store the queue in")
flag.IntVar(&segmentSize, "size", 50, "number of entries for the queue")
}
// The "webserver" component
func runserver(q *dque.DQue) {
http.HandleFunc("/webhook", func(w http.ResponseWriter, r *http.Request) {
// A new decoder for each call, as we want to have a new LimitReader
// for each call. This is a simple, albeit a bit crude method to prevent
// accidental or malicious overload of your server.
dec := json.NewDecoder(io.LimitReader(r.Body, 4096))
defer r.Body.Close()
c := &webhookContent{}
if err := dec.Decode(c); err != nil {
log.Printf("reading body: %s", err)
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
// When the content is successfully decoded, we can persist it into
// our queue.
if err := q.Enqueue(c); err != nil {
log.Printf("enqueueing webhook data: %s", err)
// PROPER ERROR HANDLING IS MISSING HERE
}
})
http.ListenAndServe(bind, nil)
}
func main() {
flag.Parse()
var (
q *dque.DQue
err error
)
if !dirExists(queueDir) {
if err = os.MkdirAll(queueDir, 0750); err != nil {
log.Fatalf("creating queue dir: %s", err)
}
}
if !dirExists(filepath.Join(queueDir, "webhooks")) {
q, err = dque.New("webhooks", queueDir, segmentSize, func() interface{} { return &webhookContent{} })
} else {
q, err = dque.Open("webhooks", queueDir, segmentSize, func() interface{} { return &webhookContent{} })
}
if err != nil {
log.Fatalf("setting up queue: %s", err)
}
defer q.Close()
go runserver(q)
var (
// Placeholder during event loop
i interface{}
// Payload
w *webhookContent
// Did the type assertion succeed
ok bool
)
for {
// We peek only. The semantic of this is that
// you can already access the next item in the queue
// without removing it from the queue and "mark" it as read.
// We use PeekBlock since we want to wait for an item in the
// queue to be available.
if i, err = q.PeekBlock(); err != nil {
// If we can not peek, something is SERIOUSLY wrong.
log.Fatalf("reading from queue: %s", err)
}
if w, ok = i.(*webhookContent); !ok {
// If the type assertion fails, something is seriously wrong, too.
log.Fatalf("reading from queue: %s", err)
}
if err = doSomethingUseful(w); err != nil {
log.Printf("Something went wrong: %s", err)
log.Println("I strongly suggest entering an incremental backoff!")
continue
}
// We did something useful, so we can dequeue the item we just processed from the queue.
q.Dequeue()
}
}
func doSomethingUseful(w *webhookContent) error {
log.Printf("Instead of this log message, you can do something useful with: %#v", w)
return nil
}
func dirExists(path string) bool {
fileInfo, err := os.Stat(path)
if err == nil {
return fileInfo.IsDir()
}
return false
}
Now when you do something like:
$ curl -X POST --data '{"Foo":"Baz","Bar":42}' http://localhost:8080/webhook
you should get a log entry like
2020/04/18 11:34:23 Instead of this log message, you can do something useful with: &main.webhookContent{Foo:"Baz", Bar:42}
Note that See GitLab 15.7 (December 2022) implements an opposite approach:
Automatic disabling of failing webhooks
To protect GitLab and users across the system from any potential abuse or misuse, we’ve implemented a feature to disable webhooks that fail consistently.
Webhooks that return response codes in the 5xx range are understood to be failing intermittently and are temporarily disabled. These webhooks are initially disabled for 1 minute, which is extended on each retry up to a maximum of 24 hours.
Webhooks that fail with 4xx errors are permanently disabled.
All project owners and maintainers are alerted in the app to investigate and re-enable any failed webhooks.
This feature is now available on GitLab.com and self-managed instances along with feature enhancements including handling cold starts.
See Epic and Documentation.
So not only sending back "an invalid HTTP response" would not work, it would result in a disabled webhook, starting with GitLab 15.7+.

How to authenticate to Google Cloud Printing

I'm making an app that needs to send printing jobs through Google Cloud Printing to two printers owned by me (i.e., the printers are always the same one and does not belong to the user). I've set up the printers with Google Cloud Printing and it is now accessible from my Google Account.
Now, how can I access this account's printers through the API? I have found some documentation here that says that I need to authenticate myself when making requests. It seems to me that the authentication should be done with OAuth2. But the instructions on how to do that are lacking for a beginner. I've gotten so far as to getting my OAuth client ID and secret (step 1 in the OAuth link). But for step 2, I have no idea what to do.
It says:
Before your application can access private data using a Google
API, it must obtain an access token that grants access to that API. A
single access token can grant varying degrees of access to multiple
APIs.
But doesn't explain how to obtain this access token. I looked at this SO question where OP seems to have been able to get this access token, but I can't understand how he did it.
Could someone please explain how to get an access token to use with Google Cloud Printing? Or a good resource which explains how?
PS. The printing functionality is triggered by a firebase function. Would this help us get the access token, considering firebase is also made by Google?
I ran into the same issue and came up with this two-step solution:
Create an OAuth2 client in your Google Cloud Console as described here
and download its client credentials from the console and copy & past its json content to credJSON in the code snippet below.
Run the code below.
Follow the auth link and authorize your OAuth2 client to access Googel Cloud Printers with your Google account.
Copy & paste the auth code to the script
Once you obtained a refresh token make sure to store it in the variable refreshToken
Don't forget to update the proxy name.
package main
import (
"context"
"fmt"
"log"
"github.com/google/cloud-print-connector/gcp"
"github.com/google/cloud-print-connector/lib"
"github.com/google/uuid"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
)
var (
credJSON = ``
refreshToken = ""
// Find the proxy in the Advanced Details of your printer at https://www.google.com/cloudprint#printers
proxy = "HP"
)
func main() {
// Obtain the OAuth config
config, err := google.ConfigFromJSON([]byte(credJSON), gcp.ScopeCloudPrint)
if err != nil {
log.Fatalf("Failed to obtain OAuth config: %v", err)
}
// If no request token is present, obtain a new one
if refreshToken == "" {
// Get the auth link
authLink := config.AuthCodeURL(uuid.New().String(), oauth2.AccessTypeOffline)
log.Printf("Follow the link to obtain an auth code: %s", authLink)
fmt.Printf("Paste your auth code here: ")
var code string
fmt.Scanln(&code)
// Get a token form the auth code
token, err := config.Exchange(context.Background(), code, oauth2.AccessTypeOffline)
if err != nil {
log.Fatalf("Failed to obtain OAuth token: %v", err)
}
if token.RefreshToken != "" {
refreshToken = token.RefreshToken
} else {
refreshToken = token.AccessToken
}
log.Printf("Refresh token: %s", refreshToken)
}
// Connect to Google Cloud Print
jobCh := make(chan *lib.Job)
client, err := gcp.NewGoogleCloudPrint(lib.DefaultConfig.GCPBaseURL, refreshToken, refreshToken, proxy, config.ClientID, config.ClientSecret, config.Endpoint.AuthURL, config.Endpoint.TokenURL, lib.DefaultConfig.NativeJobQueueSize, jobCh, true)
if err != nil {
log.Fatalf("Failed to connect to GCP: %v", err)
}
// List all printers
printers, _, err := client.ListPrinters()
if err != nil {
log.Fatalf("Failed to list printers: %v", err)
}
for _, p := range printers {
log.Printf("Name: %s UUID: %s", p.Name, p.UUID)
}
}
Please refer to following documentation:
https://developers.google.com/identity/protocols/OAuth2ServiceAccount?authuser=1
I followed the same steps specified in the doc and was able to obtain the access token. First make Google Service Account, select furnish new private key. You ll have service account email addresss and private key. Using these credentials, you can obtain your access token. Below is the source code in Golang , this ll surely help you.
package main
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"net/http"
"encoding/json"
"bytes"
)
type MyCustomClaims struct {
Scope string `json:"scope,omitempty"`
jwt.StandardClaims
}
type Toke struct {
Access string `json:"access_token,omitempty"`
Type string `json:"token_type,omitempty"`
Expire string `json:"expires_in,omitempty"`
}
func main() {
key := []byte("<your private key>")
key1, _ := jwt.ParseRSAPrivateKeyFromPEM(key)
claims := MyCustomClaims{
"https://www.googleapis.com/auth/cloudprint",
jwt.StandardClaims{
IssuedAt: <currrent-epoch-time>, // eg 1234566000
ExpiresAt: <currrent-epoch-time + 3600>, // 3600 secs = 1hour, so expires in 1 hour, eg 1234569600
Issuer: "<your service account email>",
Audience: "https://www.googleapis.com/oauth2/v4/token",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
ss, err := token.SignedString(key1)
if err != nil {
fmt.Println(err)
}
fmt.Println(ss)
url := "https://www.googleapis.com/oauth2/v4/token"
any := "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" + ss
a := []byte(any)
b := bytes.NewBuffer(a)
var tok Toke
req, err := http.NewRequest("POST", url, b)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
} else {
json.NewDecoder(resp.Body).Decode(&tok)
}
fmt.Println("----------- Access Token -----------------")
fmt.Println("Access: ", tok.Access)
}

FCM HTTP v1: how to get access token using Go?

In order to send Firebase Cloud Messaging with Go, we need to place the access token in the HTTP request header.
On Firebase documentation,
there are examples on how to retrieve the access token using Node.JS, Python and Java:
https://firebase.google.com/docs/cloud-messaging/auth-server
can anyone show the get the access token using Go?
There seems to be many Go packages about Firebase/Google authentication. It's very confusing to understand which ones should be used:
firebase.google.com/go
firebase.google.com/go/auth
github.com/firebase/firebase-admin-go
google.golang.org/api/option
golang.org/x/oauth2
golang.org/x/oauth2/google
github.com/google/google-api-go-client
I'm working on the go firebase SDK to add FCM HTTP v1.
For now it's almost finished, i have to write tests and integration tests, you can check the code here : https://github.com/chemidy/firebase-admin-go/tree/fcm/messaging
I will finish tests and send a PR maybe next week, (it's a little bit tricky to test on ios + android + web)
FCM httpv1 use JSON file for it credentials.
Download it first, then move it to your project.
download JSON credentials in your firebase
second, do :
go get "golang.org/x/oauth2/google"
then use this method to get token
const firebaseScope = "https://www.googleapis.com/auth/firebase.messaging"
type tokenProvider struct {
tokenSource oauth2.TokenSource
}
// newTokenProvider function to get token for fcm-send
func newTokenProvider(credentialsLocation string) (*tokenProvider, error) {
jsonKey, err := ioutil.ReadFile(credentialsLocation)
if err != nil {
return nil, errors.New("fcm: failed to read credentials file at: " + credentialsLocation)
}
cfg, err := google.JWTConfigFromJSON(jsonKey, firebaseScope)
if err != nil {
return nil, errors.New("fcm: failed to get JWT config for the firebase.messaging scope")
}
ts := cfg.TokenSource(context.Background())
return &tokenProvider{
tokenSource: ts,
}, nil
}
func (src *tokenProvider) token() (string, error) {
token, err := src.tokenSource.Token()
if err != nil {
return "", errors.New("fcm: failed to generate Bearer token")
}
return token.AccessToken, nil
}
Then call it in your FCM-Send method :
tp, err := newTokenProvider("yourJSONFileLocation")
if err != nil {
return nil, err
}
token, err := tp.token()
if err != nil {
return nil, err
}
last, add it to header :
r.Header.Add("Authorization", "Bearer "+token)
Done.
i am put header Authorization by used volly
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headerMap = new HashMap<String, String>();
headerMap.put("Content-Type", "application/json");
headerMap.put("Authorization", "Bearer " + key);
Log.v(TAG,"getHeaders "+headerMap);
return headerMap;
}
but send Authorization=Bearer Add..
how change to current
Authorization:Bearer

Redirects return http: multiple response.WriteHeader calls

I am using Jon Calhoun's Go MVC framework from github.
The framework uses julienschmidt/httprouter as its only dependency.
I have a similar main method as found in the example:
func main() {
//register routes
router := httprouter.New()
//default
router.GET("/", controllers.Login.Perform(controllers.Login.Index))
//login
router.GET("/login", controllers.Login.Perform(controllers.Login.Login))
router.POST("/login", controllers.Login.Perform(controllers.Login.PostLogin))
//dashboard
router.GET("/dashboard", controllers.Dashboard.Perform(controllers.Dashboard.Index))
//listen and handle requests
log.Fatal(http.ListenAndServe(":"+helpers.ReadConfig("port_http"), router))
}
I make a post to the login url, and it calls the following method:
func (self LoginController) PostLogin(w http.ResponseWriter, r *http.Request, ps httprouter.Params) error {
//create our api url
var url = helpers.ReadConfig("api") + "login"
//fill model to post
login := models.LoginModel{
Password: r.FormValue("password"),
Email: r.FormValue("username"),
}
//render json from model
bytes, err := json.Marshal(login)
if err != nil {
panic(err)
}
//post to the API helpers
var resp = helpers.ApiPost(url, r, string(bytes))
//check response if successful
if resp.Code != constants.ApiResp_Success {
//TODO: Handle API Errors
login.Password = ""
errors := make(map[int]string)
errors[1] = "Please provide valid credntials."
login.Common = models.CommonModel{
ErrorList: errors,
}
return views.Login.Index.Render(w, login, helpers.AcceptsGzip(r))
}
log.Println("---Redirect--")
http.Redirect(w, r, "/dashboard", 307)
log.Println("-----")
return views.Dashboard.Index.Render(w, login, helpers.AcceptsGzip(r))
}
Basically, if the login was not correct I return the same view. If the login is correct I want to redirect to another method in a different controller.
However when I call http.Redirect(w, r, "/dashboard", 307), it returns the following error:
http: multiple response.WriteHeader calls
I'm not sure exactly why this is happening, but I suspect that it has something to do with my listener calling the Perform function, which creates a http.handler, as shown below.
func (c *Controller) Perform(a Action) httprouter.Handle {
return httprouter.Handle(
func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
//set response headers
//TODO: set appropriate responce headers
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Cache-Control", "public, max-age=0")
w.Header().Set("Token", "NOT-A-VALID-TOKEN")
w.WriteHeader(200)
if err := a(w, r, ps); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
})
}
Does anyone have any idea how to redirect using this MVC framework? Or have a one off solution?
http.ResponseWriter's WriteHeader method can only be called once per HTTP response, for obvious reasons: You can only have a single response code, and you can only send the headers once.
The error you see means that it is called a second time on the same response.
Your middleware calls:
w.WriteHeader(200)
Then your handler also calls:
http.Redirect(w, r, "/dashboard", 307)
log.Println("-----")
return views.Dashboard.Index.Render(w, login, helpers.AcceptsGzip(r))
Your middleware should never call WriteHeader, until after the fate of the response is known.
Further, without knowing about your particular MVC framework, it seems possible that after you send the 307 status, then you also tell the MVC framework to render a response, which may also call WriteHeader again.

Resources