Last active
January 17, 2020 16:08
-
-
Save danyanya/73d679cdce75891d8ba60e69ed0e8223 to your computer and use it in GitHub Desktop.
golang vk auth for account bdate, email, sex, etc (based on https://github.com/kovetskiy/go-vk)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"encoding/json" | |
"errors" | |
"fmt" | |
"io/ioutil" | |
"log" | |
"net/http" | |
"net/url" | |
"os" | |
"strings" | |
) | |
const ( | |
//actual version | |
apiVersion = "5.103" | |
//url for authorization | |
authUrl = "https://oauth.vk.com/authorize" | |
//url for get access token | |
accessTokenUrl = "https://oauth.vk.com/access_token" | |
//url for execute methods | |
methodUrl = "https://api.vk.com/method/" | |
) | |
// main type of package | |
type Api struct { | |
AccessToken AccessToken | |
} | |
// OAuth | |
type Auth struct { | |
AppId string | |
AppSecret string | |
Permissions []string | |
RedirectUri string | |
Display string | |
} | |
// Security token | |
type AccessToken struct { | |
Token string `json:"access_token"` | |
ExpiresIn int64 `json:"expires_in"` | |
UserId int64 `json:"user_id"` | |
} | |
// Typical vk.com error contains two fields - error, error_description. | |
// And that's great. | |
type AccessError struct { | |
Summary string `json:"error"` | |
Description string `json:"error_description"` | |
} | |
func (e AccessError) Error() string { | |
return fmt.Sprintf("%s: %s", e.Summary, e.Description) | |
} | |
// you can specify pkg logger for debug library | |
type Logger interface { | |
Print(v ...interface{}) | |
Printf(format string, v ...interface{}) | |
Println(v ...interface{}) | |
} | |
var logger Logger | |
func SetLogger(l Logger) { | |
logger = l | |
} | |
func logf(format string, v ...interface{}) { | |
if logger != nil { | |
logger.Printf(format, v...) | |
} | |
} | |
// Get authorization url for user redirection, more: | |
// https://vk.com/dev/auth_sites | |
func (auth Auth) GetAuthUrl() (string, error) { | |
logf("processing GetAuthUrl of %+v", auth) | |
logf("trying to parse '%s'", authUrl) | |
urlPieces, err := url.Parse(authUrl) | |
if err != nil { | |
logf("an error occured during parsing url: %s", err) | |
return "", err | |
} | |
query := urlPieces.Query() | |
query.Set("client_id", auth.AppId) | |
query.Set("scope", strings.Join(auth.Permissions, ",")) | |
query.Set("redirect_uri", auth.RedirectUri) | |
query.Set("display", auth.Display) | |
query.Set("response_type", "code") | |
logf("auth url query: %+v", query) | |
urlPieces.RawQuery = query.Encode() | |
authUrl := urlPieces.String() | |
logf("auth url: '%s'", authUrl) | |
return authUrl, nil | |
} | |
// Get access token for 'Server Auth' | |
func (auth Auth) GetAccessToken(code string) (accessToken AccessToken, err error) { | |
logf("processing GetAccessToken of %+v", auth) | |
logf("trying to parse '%s'", accessTokenUrl) | |
urlPieces, err := url.Parse(accessTokenUrl) | |
if err != nil { | |
logf("an error occured during parsing url: %s", err) | |
return | |
} | |
query := urlPieces.Query() | |
query.Set("client_id", auth.AppId) | |
query.Set("client_secret", auth.AppSecret) | |
query.Set("code", code) | |
query.Set("redirect_uri", auth.RedirectUri) | |
urlPieces.RawQuery = query.Encode() | |
logf("access token url query: %+v", query) | |
tokenUrl := urlPieces.String() | |
logf("trying to get content of '%s'", tokenUrl) | |
resp, err := http.Get(tokenUrl) | |
if err != nil { | |
logf("an error occured during execution GET request: %s", err) | |
return | |
} | |
body, err := ioutil.ReadAll(resp.Body) | |
defer resp.Body.Close() | |
logf("received content: %s", body) | |
logf("trying to unmarshal response to accessToken struct") | |
err = json.Unmarshal(body, &accessToken) | |
if err != nil { | |
logf("an error occured during json decoding: %s", err) | |
return | |
} | |
logf("unmarshaled data (accessToken): %+v", accessToken) | |
if accessToken.ExpiresIn != 0 { | |
logf("all is ok, accessToken: %+v", accessToken) | |
return | |
} | |
//okay, something is wrong... may be it's typical error? | |
logf("accessToken.ExpiresIn = 0, trying recognize error") | |
accessError := AccessError{} | |
err = json.Unmarshal(body, &accessError) | |
if err != nil { | |
logf("an error occured during json decoding: %s", err) | |
return | |
} | |
logf("unmarshaled data (accessError): %+v", accessToken) | |
//okay we are have problems with authorization... | |
if accessError.Summary != "" { | |
logf("problem with a getting access: %s", accessError) | |
return AccessToken{}, accessError | |
} | |
logf("couldn't recognize error") | |
return AccessToken{}, errors.New(fmt.Sprintf( | |
"Couldn't recognize error, body: %s", body)) | |
} | |
// do you want make a request? It's it. | |
func (api *Api) Request(method string, params map[string]string) ( | |
response map[string]interface{}, err error) { | |
logf("proccessing request method '%s' with params %+v of api %+v", | |
method, params, api) | |
rawUrl := methodUrl + method | |
logf("raw url for execution method is '%s'", rawUrl) | |
urlPieces, err := url.Parse(rawUrl) | |
if err != nil { | |
logf("an error occured during parsing url: %s", err) | |
return | |
} | |
logf("begin to prepare the data for the request") | |
query := urlPieces.Query() | |
for name, value := range params { | |
query.Set(name, value) | |
} | |
logf("query w/o access_token and version: %+v", query) | |
if api.AccessToken.Token != "" { | |
query.Set("access_token", api.AccessToken.Token) | |
} | |
query.Set("v", apiVersion) | |
logf("query with access_token and version: %+v", query) | |
urlPieces.RawQuery = query.Encode() | |
resultUrl := urlPieces.String() | |
logf("trying to get content of '%s'", resultUrl) | |
resp, err := http.Get(resultUrl) | |
if err != nil { | |
logf("an error occured during execution GET request: %s", err) | |
return | |
} | |
defer resp.Body.Close() | |
body, err := ioutil.ReadAll(resp.Body) | |
if err != nil { | |
logf("an error occured during reading body buffer: %s", err) | |
return | |
} | |
logf("received: %s:", body) | |
err = json.Unmarshal(body, &response) | |
if err != nil { | |
logf("an error occured during json decoding: %s", err) | |
} else { | |
logf("response: %+v", response) | |
} | |
//may be catch {"error":{"error_code":15...blahblah? | |
return | |
} | |
func main() { | |
SetLogger(log.New(os.Stdout, "VK: ", log.Ldate|log.Ltime|log.Lshortfile)) | |
auth := Auth{ | |
AppId: "APP_ID", | |
AppSecret: "APP_SECRET", | |
Permissions: []string{"email", "friends"}, | |
RedirectUri: "http://localhost:5000/", | |
Display: "page", // more on https://vk.com/dev/auth_sites | |
} | |
authUrl, err := auth.GetAuthUrl() | |
if err != nil { | |
panic(err) | |
} | |
fmt.Printf(authUrl) | |
var token AccessToken | |
var api Api | |
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | |
var err error | |
keys, ok := r.URL.Query()["code"] | |
if (!ok || len(keys[0]) < 1) && len(token.Token) == 0 { | |
http.Redirect(w, r, authUrl, 302) | |
return | |
} | |
if len(token.Token) == 0 { | |
code := keys[0] | |
token, err = auth.GetAccessToken(code) | |
if err != nil { | |
return | |
} | |
log.Println(code, token) | |
api = Api{token} | |
} | |
q := map[string]string{ | |
"fields": "bdate,city,home_town,contacts,sex", | |
} | |
rsp, err := api.Request("users.get", q) | |
fmt.Fprintf(w, "account data: %+v err: %+v", rsp, err) | |
}) | |
http.ListenAndServe(":5000", nil) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment