init: can do full login:

ooo muh gut

Signed-off-by: HeshamTB <hishaminv@gmail.com>
This commit is contained in:
HeshamTB 2024-04-29 17:38:11 +03:00
commit 9c1014ac72
Signed by: Hesham
GPG Key ID: 74876157D199B09E
7 changed files with 284 additions and 0 deletions

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module mystcapi
go 1.22.2

View File

@ -0,0 +1,5 @@
package constants
const (
CONTENT_TYPE_JSON = "application/json"
)

10
internal/urls/common.go Normal file
View File

@ -0,0 +1,10 @@
package urls
const (
URL_MYSTC_API_BASE = "https://mystc.stc.com.sa"
PATH_API_AUTH_BASE = URL_MYSTC_API_BASE + "/api/mystc-api-authentication"
PATH_PHONES_LIST = PATH_API_AUTH_BASE + "/phones-list"
PATH_LOGIN = PATH_API_AUTH_BASE + "/login"
PATH_LOGIN_VERIFICATION = PATH_API_AUTH_BASE + "/login-verification"
)

75
main.go Normal file
View File

@ -0,0 +1,75 @@
package main
import (
"bufio"
"flag"
"fmt"
"mystcapi/pkg/endpoints"
"mystcapi/pkg/models"
"os"
"strings"
)
const URL_MYSTC_API_BASE = "https://mystc.stc.com.sa"
const PATH_PHONES_LIST = "/api/mystc-api-authentication/phones-list"
func main() {
id := flag.String("id", "", "National ID or Iqama")
pw := flag.String("password", "", "MySTC Password")
flag.Parse()
phones, err := endpoints.GetPhonesList(*id, *pw)
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
reader := bufio.NewReader(os.Stdin)
var selectedNumber models.PhoneNumber
mainloop:
for {
for idx, n := range phones.PhoneNumbers {
fmt.Printf("%d: %s %s\n", idx, n.Number, n.Type.String())
}
input, err := reader.ReadString('\n')
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
input = strings.Trim(input, "\n")
for idx, n := range phones.PhoneNumbers {
if input == fmt.Sprint(idx) {
selectedNumber = n
break mainloop
}
}
}
fmt.Printf("selectedNumber: %v\n", selectedNumber)
loginOTP, err := endpoints.RequestLoginOTP(phones.LoginToken, selectedNumber.Number)
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
fmt.Println("OTP Sent!")
fmt.Printf("loginOTP: %v\n", loginOTP)
fmt.Print("Enter OTP: ")
otp, err := reader.ReadString('\n')
otp = strings.Trim(otp, "\n")
login, err := endpoints.VerifyLoginOTP(*id, loginOTP.OtpToken, otp)
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
fmt.Println("Logged in!")
fmt.Printf("login: %v\n", login)
}

9
pkg/endpoints/common.go Normal file
View File

@ -0,0 +1,9 @@
package endpoints
import "net/http"
var client *http.Client
func init() {
client = &http.Client{}
}

123
pkg/endpoints/login.go Normal file
View File

@ -0,0 +1,123 @@
package endpoints
import (
"bytes"
"encoding/json"
"fmt"
"io"
"mystcapi/internal/constants"
"mystcapi/internal/urls"
"mystcapi/pkg/models"
"net/http"
)
func GetPhonesList(id, password string) (models.LoginPhoneListResponse, error) {
phoneList := models.LoginPhoneListResponse{}
reqJson, err := json.Marshal(models.LoginPhoneListRequest{
Username: id,
Password: password,
})
if err != nil {
return phoneList, err
}
resp, err := client.Post(
urls.PATH_PHONES_LIST,
constants.CONTENT_TYPE_JSON,
bytes.NewReader(reqJson),
)
if err != nil {
return phoneList, err
}
if resp.StatusCode != http.StatusOK {
return phoneList, fmt.Errorf("http: Login got %d", resp.StatusCode)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return phoneList, err
}
err = json.Unmarshal(body, &phoneList)
return phoneList, err
}
func RequestLoginOTP(loginToken, phoneNumber string) (models.LoginResponse, error) {
loginResp := models.LoginResponse{}
reqJson, err := json.Marshal(
models.LoginRequest{
LoginToken: loginToken,
PhoneNumber: phoneNumber,
},
)
if err != nil {
return loginResp, err
}
resp, err := client.Post(
urls.PATH_LOGIN,
constants.CONTENT_TYPE_JSON,
bytes.NewReader(reqJson),
)
if err != nil {
return loginResp, err
}
if resp.StatusCode != http.StatusOK {
return loginResp, fmt.Errorf("http: RequestLoginOTP got %d", resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return loginResp, err
}
err = json.Unmarshal(body, &loginResp)
return loginResp, err
}
func VerifyLoginOTP(
username, otpToken,
otp string) (models.LoginVerificationResponse, error) {
loginVerResp := models.LoginVerificationResponse{}
reqJson, err := json.Marshal(
models.LoginVerificationRequest{
Username: username,
OtpToken: otpToken,
Otp: otp,
},
)
if err != nil {
return loginVerResp, err
}
resp, err := client.Post(
urls.PATH_LOGIN_VERIFICATION,
constants.CONTENT_TYPE_JSON,
bytes.NewReader(reqJson),
)
if err != nil {
return loginVerResp, err
}
if resp.StatusCode != http.StatusOK {
return loginVerResp, fmt.Errorf("http: RequestLoginOTP got %d", resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return loginVerResp, err
}
err = json.Unmarshal(body, &loginVerResp)
return loginVerResp, err
}

59
pkg/models/login.go Normal file
View File

@ -0,0 +1,59 @@
package models
type LoginPhoneListRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
type LoginPhoneListResponse struct {
PhoneNumbers []PhoneNumber `json:"phoneNumbers"`
LoginToken string `json:"loginToken"`
}
type PhoneNumberType string
var (
POSTPAID PhoneNumberType = "1"
PREPAID PhoneNumberType = "2"
)
func (p PhoneNumberType) String() string {
switch p {
case "1":
return "POSTPAID"
case "2":
return "PREPAID"
default:
return "UNKOWN"
}
}
type PhoneNumber struct {
Number string `json:"number"`
Type PhoneNumberType `json:"type"`
Contact string `json:"contact"`
}
type LoginRequest struct {
LoginToken string `json:"loginToken"`
PhoneNumber string `json:"phoneNumber"`
}
type LoginResponse struct {
OtpToken string `json:"otpToken"`
ExpiresIn string `json:"expiresIn"`
TokenType string `json:"tokenType"`
}
type LoginVerificationRequest struct {
Username string `json:"username"`
OtpToken string `json:"otpToken"`
Otp string `json:"otp"`
}
type LoginVerificationResponse struct {
AccessToken string `json:"accessToken"`
TokenType string `json:"tokenType"`
ExpiresIn string `json:"expiresIn"`
RefreshToken string `json:"refreshToken"`
}