In dev account I'm using "/oauth2/default/v1/authorize" and everything works fine.
However, using this URI with our production account redirects me to a 400 error page with the message "The requested feature is not enabled in this environment".
I saw where some people have said to remove the "http://{yourOktaOrg}/oauth2/default" from the URI, which I tried, but got a 404 error.
Then I got another ref saying removing "default" will not led you to paid feature. This help me to bypass login and redirect it to the callback handler. But further I am using exchange code API "http://{yourOktaOrg}/oauth2/v1/token" which will help me to get IdToken.
because of not getting token verification get failed and I am not able to save session.
I tried with "http://{yourOktaOrg}/v1/token"
Any help would be greatly appreciated.
If I don't want to go for paid service will that be possible ?
Type of Application: Server-side (golang),
here is my code:
package middleware
import (
"bytes"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"github.com/gorilla/sessions"
verifier "github.com/okta/okta-jwt-verifier-golang"
)
type Exchange struct {
Error string `json:"error,omitempty"`
ErrorDescription string `json:"error_description,omitempty"`
AccessToken string `json:"access_token,omitempty"`
TokenType string `json:"token_type,omitempty"`
ExpiresIn int `json:"expires_in,omitempty"`
Scope string `json:"scope,omitempty"`
IdToken string `json:"id_token,omitempty"`
}
type OKTADetails struct {
CLIENT_ID string `json:"client_id"`
CLIENT_SECRET string `json:"client_secret"`
ISSUER string `json:"issuer"`
RedirectURI string `json:"redirect_uri"`
}
var (
sessionStore = sessions.NewCookieStore([]byte("okta-hosted-login-session-store"))
state = generateState()
nonce = "NonceNotSetYet"
okta OKTADetails
)
func GetOKTADetails(client_id, client_secret, issuer, redirect_uri string) {
okta.CLIENT_ID = client_id
okta.CLIENT_SECRET = client_secret
okta.ISSUER = issuer
okta.RedirectURI = redirect_uri
}
func generateState() string {
// Generate a random byte array for state paramter
b := make([]byte, 16)
rand.Read(b)
return hex.EncodeToString(b)
}
func GenerateNonce() (string, error) {
nonceBytes := make([]byte, 32)
_, err := rand.Read(nonceBytes)
if err != nil {
return "", fmt.Errorf("could not generate nonce")
}
return base64.URLEncoding.EncodeToString(nonceBytes), nil
}
func LoginHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Cache-Control", "no-cache") // See https://github.com/okta/samples-golang/issues/20
nonce, _ = GenerateNonce()
var redirectPath string
q := r.URL.Query()
q.Add("client_id", okta.CLIENT_ID)
q.Add("response_type", "code")
q.Add("response_mode", "query")
q.Add("scope", "openid profile email")
q.Add("redirect_uri", okta.RedirectURI)
q.Add("state", state)
q.Add("nonce", nonce)
redirectPath = okta.ISSUER + "/v1/authorize?" + q.Encode()
http.Redirect(w, r, redirectPath, http.StatusFound)
}
func AuthCodeCallbackHandler(w http.ResponseWriter, r *http.Request) {
// Check the state that was returned in the query string is the same as the above state
if r.URL.Query().Get("state") != state {
fmt.Fprintln(w, "The state was not as expected")
return
}
// Make sure the code was provided
if r.URL.Query().Get("code") == "" {
fmt.Fprintln(w, "The code was not returned or is not accessible")
return
}
exchange := exchangeCode(r.URL.Query().Get("code"), r)
if exchange.Error != "" {
fmt.Println(exchange.Error)
fmt.Println(exchange.ErrorDescription)
return
}
session, err := sessionStore.Get(r, "okta-hosted-login-session-store")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
_, verificationError := verifyToken(exchange.IdToken)
if verificationError != nil {
fmt.Println(verificationError)
}
if verificationError == nil {
session.Values["id_token"] = exchange.IdToken
session.Values["access_token"] = exchange.AccessToken
session.Save(r, w)
}
http.Redirect(w, r, "/", http.StatusFound)
}
func LogoutHandler(w http.ResponseWriter, r *http.Request) {
session, err := sessionStore.Get(r, "okta-hosted-login-session-store")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
delete(session.Values, "id_token")
delete(session.Values, "access_token")
session.Save(r, w)
http.Redirect(w, r, "/", http.StatusFound)
}
func exchangeCode(code string, r *http.Request) Exchange {
authHeader := base64.StdEncoding.EncodeToString(
[]byte(okta.CLIENT_ID + ":" + okta.CLIENT_SECRET))
q := r.URL.Query()
q.Add("grant_type", "authorization_code")
q.Set("code", code)
q.Add("redirect_uri", okta.RedirectURI)
url := okta.ISSUER + "/v1/token?" + q.Encode()
req, _ := http.NewRequest("POST", url, bytes.NewReader([]byte("")))
h := req.Header
h.Add("Authorization", "Basic "+authHeader)
h.Add("Accept", "application/json")
h.Add("Content-Type", "application/x-www-form-urlencoded")
h.Add("Connection", "close")
h.Add("Content-Length", "0")
client := &http.Client{}
resp, _ := client.Do(req)
body, _ := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
var exchange Exchange
json.Unmarshal(body, &exchange)
return exchange
}
func IsAuthenticated(r *http.Request) bool {
session, err := sessionStore.Get(r, "okta-hosted-login-session-store")
if err != nil || session.Values["id_token"] == nil || session.Values["id_token"] == "" {
return false
}
return true
}
func verifyToken(t string) (*verifier.Jwt, error) {
tv := map[string]string{}
tv["nonce"] = nonce
tv["aud"] = okta.CLIENT_ID
jv := verifier.JwtVerifier{
Issuer: okta.ISSUER,
ClaimsToValidate: tv,
}
result, err := jv.New().VerifyIdToken(t)
if err != nil {
return nil, fmt.Errorf("%s", err)
}
if result != nil {
return result, nil
}
return nil, fmt.Errorf("token could not be verified: %s", "")
}