diff --git a/README.md b/README.md
index 03ea79e..32a4751 100644
--- a/README.md
+++ b/README.md
@@ -3,18 +3,32 @@ OpenStack Golang Client
 
 NOTE(dtroyer) Apr 2015: This repo is under heavy revision as it is being revived.
 
-stackforge/golang-client is yet another implementation of [OpenStack]
+`stackforge/golang-client` is an implementation of [OpenStack]
 (http://www.openstack.org/) API client in [Go language](http://golang.org).
-The code follows OpenStack licensing and borrows its infrastructure for code
-hosting.  It currently implements [Identity Service v2] 
+The code follows OpenStack licensing and uses the Stackforge infrastructure
+for hosting.  It currently implements [Identity Service v2] 
 (http://docs.openstack.org/api/openstack-identity-service/2.0/content/) 
 and [Object Storage v1] 
 (http://docs.openstack.org/api/openstack-object-storage/1.0/content/).
-Some API calls are not implemented initially, but the intention is to expand
-the lib over time (where pragmatic).
 
+The initial focus is on building a solid core REST Session and OpenStack
+authentication on which to build the usual API interfaces.  The architecture
+if the `Session` and authentication is similar to that used in the current
+Python Keystone client library: The `Session` object contains the HTTP
+interface methods and an authentication object that provides access to
+the auth token and service catalog.
+
+Current State
+-------------
 Code maturity is considered experimental.
 
+* The new Session object is functional and used by most of the code now.
+* The examples work.
+* The image tests work.
+* The obejct store tests do not work.
+* identity/v2/auth.go is now unused, will be kept around for a short time
+  for easier reference.
+
 Installation
 ------------
 Use `go get git.openstack.org/stackforge/golang-client.git`.  Or alternatively,
@@ -53,7 +67,7 @@ Apache v2.
 
 Contributing
 ------------
-The code repository borrows OpenStack StackForge infrastructure.
+The code repository utilizes the OpenStack StackForge infrastructure.
 Please use the [recommended workflow]
 (http://docs.openstack.org/infra/manual/developers.html#development-workflow).  If you are not a member yet,
 please consider joining as an [OpenStack contributor]
diff --git a/examples/00-authentication.go b/examples/00-authentication.go
index 941b3fb..2222be8 100644
--- a/examples/00-authentication.go
+++ b/examples/00-authentication.go
@@ -16,8 +16,10 @@ package main
 
 import (
 	"fmt"
-	"git.openstack.org/stackforge/golang-client.git/identity/v2"
+	// "git.openstack.org/stackforge/golang-client.git/identity/v2"
 	"time"
+
+	"git.openstack.org/stackforge/golang-client.git/openstack"
 )
 
 // Authentication examples.
@@ -26,42 +28,51 @@ func main() {
 
 	// Authenticate with just a username and password. The returned token is
 	// unscoped to a tenant.
-	auth, err := identity.AuthUserName(config.Host,
-		config.Username,
-		config.Password)
+	creds := openstack.AuthOpts{
+		AuthUrl:  config.Host,
+		Username: config.Username,
+		Password: config.Password,
+	}
+	auth, err := openstack.DoAuthRequest(creds)
 	if err != nil {
-		fmt.Println("There was an error authenticating:", err)
+		fmt.Println("Error authenticating username/password:", err)
 		return
 	}
-	if !auth.Access.Token.Expires.After(time.Now()) {
+	if !auth.GetExpiration().After(time.Now()) {
 		fmt.Println("There was an error. The auth token has an invalid expiration.")
 		return
 	}
 
-	// Authenticate with a username, password, tenant name.
-	auth, err = identity.AuthUserNameTenantName(config.Host,
-		config.Username,
-		config.Password,
-		config.ProjectName)
+	// Authenticate with a project name, username, password.
+	creds = openstack.AuthOpts{
+		AuthUrl:  config.Host,
+		Project:  config.ProjectName,
+		Username: config.Username,
+		Password: config.Password,
+	}
+	auth, err = openstack.DoAuthRequest(creds)
 	if err != nil {
-		fmt.Println("There was an error authenticating:", err)
+		fmt.Println("Error authenticating project/username/password:", err)
 		return
 	}
-	if !auth.Access.Token.Expires.After(time.Now()) {
+	if !auth.GetExpiration().After(time.Now()) {
 		fmt.Println("There was an error. The auth token has an invalid expiration.")
 		return
 	}
 
-	// Authenticate with a username, password, tenant id.
-	auth, err = identity.AuthUserNameTenantId(config.Host,
-		config.Username,
-		config.Password,
-		config.ProjectID)
+	// Authenticate with a project id, username, password.
+	creds = openstack.AuthOpts{
+		AuthUrl:  config.Host,
+		Project:  config.ProjectID,
+		Username: config.Username,
+		Password: config.Password,
+	}
+	auth, err = openstack.DoAuthRequest(creds)
 	if err != nil {
-		fmt.Println("There was an error authenticating:", err)
+		fmt.Println("Error authenticating project/username/password:", err)
 		return
 	}
-	if !auth.Access.Token.Expires.After(time.Now()) {
+	if !auth.GetExpiration().After(time.Now()) {
 		fmt.Println("There was an error. The auth token has an invalid expiration.")
 		return
 	}
diff --git a/examples/10-objectstore.go b/examples/10-objectstore.go
index 12d9897..5fff8b9 100644
--- a/examples/10-objectstore.go
+++ b/examples/10-objectstore.go
@@ -18,10 +18,12 @@ import (
 	"bytes"
 	"encoding/json"
 	"fmt"
-	"git.openstack.org/stackforge/golang-client.git/identity/v2"
-	"git.openstack.org/stackforge/golang-client.git/objectstorage/v1"
 	"io/ioutil"
+	"net/http"
 	"time"
+
+	"git.openstack.org/stackforge/golang-client.git/objectstorage/v1"
+	"git.openstack.org/stackforge/golang-client.git/openstack"
 )
 
 func main() {
@@ -29,45 +31,52 @@ func main() {
 
 	// Before working with object storage we need to authenticate with a project
 	// that has active object storage.
-	auth, err := identity.AuthUserNameTenantName(config.Host,
-		config.Username,
-		config.Password,
-		config.ProjectName)
+	// Authenticate with a project name, username, password.
+	creds := openstack.AuthOpts{
+		AuthUrl:  config.Host,
+		Project:  config.ProjectName,
+		Username: config.Username,
+		Password: config.Password,
+	}
+	auth, err := openstack.DoAuthRequest(creds)
 	if err != nil {
 		panicString := fmt.Sprint("There was an error authenticating:", err)
 		panic(panicString)
 	}
-	if !auth.Access.Token.Expires.After(time.Now()) {
+	if !auth.GetExpiration().After(time.Now()) {
 		panic("There was an error. The auth token has an invalid expiration.")
 	}
 
 	// Find the endpoint for object storage.
-	url := ""
-	for _, svc := range auth.Access.ServiceCatalog {
-		if svc.Type == "object-store" {
-			url = svc.Endpoints[0].PublicURL + "/"
-			break
-		}
-	}
-	if url == "" {
+	url, err := auth.GetEndpoint("object-store", "")
+	if url == "" || err != nil {
 		panic("object-store url not found during authentication")
 	}
 
-	hdr, err := objectstorage.GetAccountMeta(url, auth.Access.Token.Id)
+	// Make a new client with these creds
+	sess, err := openstack.NewSession(nil, auth, nil)
+	if err != nil {
+		panicString := fmt.Sprint("Error crating new Session:", err)
+		panic(panicString)
+	}
+
+	hdr, err := objectstorage.GetAccountMeta(sess, url)
 	if err != nil {
 		panicString := fmt.Sprint("There was an error getting account metadata:", err)
 		panic(panicString)
 	}
+	_ = hdr
 
 	// Create a new container.
-	if err = objectstorage.PutContainer(url+config.Container, auth.Access.Token.Id,
-		"X-Log-Retention", "true"); err != nil {
+	var headers http.Header = http.Header{}
+	headers.Add("X-Log-Retention", "true")
+	if err = objectstorage.PutContainer(sess, url+"/"+config.Container, headers); err != nil {
 		panicString := fmt.Sprint("PutContainer Error:", err)
 		panic(panicString)
 	}
 
 	// Get a list of all the containers at the selected endoint.
-	containersJson, err := objectstorage.ListContainers(0, "", url, auth.Access.Token.Id)
+	containersJson, err := objectstorage.ListContainers(sess, 0, "", url)
 	if err != nil {
 		panic(err)
 	}
@@ -93,12 +102,13 @@ func main() {
 	}
 
 	// Set and Get container metadata.
-	if err = objectstorage.SetContainerMeta(url+config.Container, auth.Access.Token.Id,
-		"X-Container-Meta-fubar", "false"); err != nil {
+	headers = http.Header{}
+	headers.Add("X-Container-Meta-fubar", "false")
+	if err = objectstorage.SetContainerMeta(sess, url+"/"+config.Container, headers); err != nil {
 		panic(err)
 	}
 
-	hdr, err = objectstorage.GetContainerMeta(url+config.Container, auth.Access.Token.Id)
+	hdr, err = objectstorage.GetContainerMeta(sess, url+"/"+config.Container)
 	if err != nil {
 		panicString := fmt.Sprint("GetContainerMeta Error:", err)
 		panic(panicString)
@@ -115,13 +125,14 @@ func main() {
 		panic(err)
 	}
 
+	headers = http.Header{}
+	headers.Add("X-Container-Meta-fubar", "false")
 	object := config.Container + "/" + srcFile
-	if err = objectstorage.PutObject(&fContent, url+object, auth.Access.Token.Id,
-		"X-Object-Meta-fubar", "false"); err != nil {
+	if err = objectstorage.PutObject(sess, &fContent, url+"/"+object, headers); err != nil {
 		panic(err)
 	}
-	objectsJson, err := objectstorage.ListObjects(0, "", "", "", "",
-		url+config.Container, auth.Access.Token.Id)
+	objectsJson, err := objectstorage.ListObjects(sess, 0, "", "", "", "",
+		url+"/"+config.Container)
 
 	type objectType struct {
 		Name, Hash, Content_type, Last_modified string
@@ -143,12 +154,13 @@ func main() {
 	}
 
 	// Manage object metadata
-	if err = objectstorage.SetObjectMeta(url+object, auth.Access.Token.Id,
-		"X-Object-Meta-fubar", "true"); err != nil {
+	headers = http.Header{}
+	headers.Add("X-Object-Meta-fubar", "true")
+	if err = objectstorage.SetObjectMeta(sess, url+"/"+object, headers); err != nil {
 		panicString := fmt.Sprint("SetObjectMeta Error:", err)
 		panic(panicString)
 	}
-	hdr, err = objectstorage.GetObjectMeta(url+object, auth.Access.Token.Id)
+	hdr, err = objectstorage.GetObjectMeta(sess, url+"/"+object)
 	if err != nil {
 		panicString := fmt.Sprint("GetObjectMeta Error:", err)
 		panic(panicString)
@@ -159,7 +171,7 @@ func main() {
 	}
 
 	// Retrieve an object and check that it is the same as what as uploaded.
-	_, body, err := objectstorage.GetObject(url+object, auth.Access.Token.Id)
+	_, body, err := objectstorage.GetObject(sess, url+"/"+object)
 	if err != nil {
 		panicString := fmt.Sprint("GetObject Error:", err)
 		panic(panicString)
@@ -170,24 +182,23 @@ func main() {
 	}
 
 	// Duplication (Copy) an existing object.
-	if err = objectstorage.CopyObject(url+object, "/"+object+".dup", auth.Access.Token.Id); err != nil {
+	if err = objectstorage.CopyObject(sess, url+"/"+object, "/"+object+".dup"); err != nil {
 		panicString := fmt.Sprint("CopyObject Error:", err)
 		panic(panicString)
 	}
 
 	// Delete the objects.
-	if err = objectstorage.DeleteObject(url+object, auth.Access.Token.Id); err != nil {
+	if err = objectstorage.DeleteObject(sess, url+"/"+object); err != nil {
 		panicString := fmt.Sprint("DeleteObject Error:", err)
 		panic(panicString)
 	}
-	if err = objectstorage.DeleteObject(url+object+".dup", auth.Access.Token.Id); err != nil {
+	if err = objectstorage.DeleteObject(sess, url+"/"+object+".dup"); err != nil {
 		panicString := fmt.Sprint("DeleteObject Error:", err)
 		panic(panicString)
 	}
 
 	// Delete the container that was previously created.
-	if err = objectstorage.DeleteContainer(url+config.Container,
-		auth.Access.Token.Id); err != nil {
+	if err = objectstorage.DeleteContainer(sess, url+"/"+config.Container); err != nil {
 		panicString := fmt.Sprint("DeleteContainer Error:", err)
 		panic(panicString)
 	}
diff --git a/examples/30-image-v1.go b/examples/30-image-v1.go
index e7027dd..49ae777 100644
--- a/examples/30-image-v1.go
+++ b/examples/30-image-v1.go
@@ -16,10 +16,11 @@ package main
 
 import (
 	"fmt"
-	"git.openstack.org/stackforge/golang-client.git/identity/v2"
-	"git.openstack.org/stackforge/golang-client.git/image/v1"
 	"net/http"
 	"time"
+
+	"git.openstack.org/stackforge/golang-client.git/image/v1"
+	"git.openstack.org/stackforge/golang-client.git/openstack"
 )
 
 // Image examples.
@@ -27,34 +28,39 @@ func main() {
 	config := getConfig()
 
 	// Authenticate with a username, password, tenant id.
-	auth, err := identity.AuthUserNameTenantName(config.Host,
-		config.Username,
-		config.Password,
-		config.ProjectName)
+	creds := openstack.AuthOpts{
+		AuthUrl:  config.Host,
+		Project:  config.ProjectName,
+		Username: config.Username,
+		Password: config.Password,
+	}
+	auth, err := openstack.DoAuthRequest(creds)
 	if err != nil {
 		panicString := fmt.Sprint("There was an error authenticating:", err)
 		panic(panicString)
 	}
-	if !auth.Access.Token.Expires.After(time.Now()) {
+	if !auth.GetExpiration().After(time.Now()) {
 		panic("There was an error. The auth token has an invalid expiration.")
 	}
 
 	// Find the endpoint for the image service.
-	url := ""
-	for _, svc := range auth.Access.ServiceCatalog {
-		if svc.Type == "image" {
-			for _, ep := range svc.Endpoints {
-				url = ep.PublicURL + "/v1"
-				break
-			}
-		}
-	}
-
-	if url == "" {
+	url, err := auth.GetEndpoint("image", "")
+	if url == "" || err != nil {
 		panic("v1 image service url not found during authentication")
 	}
 
-	imageService := image.Service{TokenID: auth.Access.Token.Id, Client: *http.DefaultClient, URL: url}
+	// Make a new client with these creds
+	sess, err := openstack.NewSession(nil, auth, nil)
+	if err != nil {
+		panicString := fmt.Sprint("Error crating new Session:", err)
+		panic(panicString)
+	}
+
+	imageService := image.Service{
+		Session: *sess,
+		Client:  *http.DefaultClient,
+		URL:     url + "/v1", // We're forcing Image v1 for now
+	}
 	imagesDetails, err := imageService.ImagesDetail()
 	if err != nil {
 		panicString := fmt.Sprint("Cannot access images:", err)
diff --git a/identity/v2/auth.go b/identity/v2/unused-auth.go
similarity index 100%
rename from identity/v2/auth.go
rename to identity/v2/unused-auth.go
diff --git a/image/v1/image.go b/image/v1/image.go
index 4937bd8..bcd46f0 100644
--- a/image/v1/image.go
+++ b/image/v1/image.go
@@ -24,7 +24,9 @@ package image
 
 import (
 	"encoding/json"
+	"errors"
 	"fmt"
+	"io/ioutil"
 	"net/http"
 	"net/url"
 
@@ -38,8 +40,8 @@ import (
 // 	imageService := image.ImageService{Client: *http.DefaultClient, TokenId: tokenId, Url: "http://imageservicelocation"}
 //  images:= imageService.Images()
 type Service struct {
+	Session openstack.Session
 	Client  http.Client
-	TokenID string
 	URL     string
 }
 
@@ -150,19 +152,22 @@ func (imageService Service) queryImages(includeDetails bool, imagesResponseConta
 	}
 
 	var headers http.Header = http.Header{}
-	headers.Set("X-Auth-Token", imageService.TokenID)
 	headers.Set("Accept", "application/json")
-	resp, err := openstack.Get(reqURL.String(), nil, &headers)
+	resp, err := imageService.Session.Get(reqURL.String(), nil, &headers)
 	if err != nil {
 		return err
 	}
 
-	err = util.CheckHTTPResponseStatusCode(resp.Resp)
+	err = util.CheckHTTPResponseStatusCode(resp)
 	if err != nil {
 		return err
 	}
 
-	if err = json.Unmarshal(resp.Body, &imagesResponseContainer); err != nil {
+	rbody, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return errors.New("aaa")
+	}
+	if err = json.Unmarshal(rbody, &imagesResponseContainer); err != nil {
 		return err
 	}
 	return nil
diff --git a/image/v1/image_test.go b/image/v1/image_test.go
index 707d07d..e49e7cb 100644
--- a/image/v1/image_test.go
+++ b/image/v1/image_test.go
@@ -22,6 +22,7 @@ import (
 	"testing"
 
 	"git.openstack.org/stackforge/golang-client.git/image/v1"
+	"git.openstack.org/stackforge/golang-client.git/openstack"
 	"git.openstack.org/stackforge/golang-client.git/testUtil"
 	"git.openstack.org/stackforge/golang-client.git/util"
 )
@@ -159,7 +160,18 @@ func testImageServiceAction(t *testing.T, uriEndsWith string, testData string, i
 	apiServer := testUtil.CreateGetJSONTestRequestServer(t, tokn, testData, anon)
 	defer apiServer.Close()
 
-	imageService := image.Service{TokenID: tokn, Client: *http.DefaultClient, URL: apiServer.URL}
+	auth := openstack.AuthToken{
+		Access: openstack.AccessType{
+			Token: openstack.Token{
+				ID: tokn,
+			},
+		},
+	}
+	sess, _ := openstack.NewSession(http.DefaultClient, auth, nil)
+	imageService := image.Service{
+		Session: *sess,
+		URL:     apiServer.URL,
+	}
 	imageServiceAction(&imageService)
 }
 
diff --git a/objectstorage/v1/objectstorage.go b/objectstorage/v1/objectstorage.go
index 2c4ae75..51444bb 100644
--- a/objectstorage/v1/objectstorage.go
+++ b/objectstorage/v1/objectstorage.go
@@ -31,65 +31,65 @@ var zeroByte = &([]byte{}) //pointer to empty []byte
 //"limit" and "marker" corresponds to the API's "limit" and "marker".
 //"url" can be regular storage or cdn-enabled storage URL.
 //It returns []byte which then needs to be unmarshalled to decode the JSON.
-func ListContainers(limit int64, marker, url, token string) ([]byte, error) {
-	return ListObjects(limit, marker, "", "", "", url, token)
+func ListContainers(session *openstack.Session, limit int64, marker, url string) ([]byte, error) {
+	return ListObjects(session, limit, marker, "", "", "", url)
 }
 
 //GetAccountMeta calls the OpenStack retrieve account metadata API using
 //previously obtained token.
-func GetAccountMeta(url, token string) (http.Header, error) {
-	return GetObjectMeta(url, token)
+func GetAccountMeta(session *openstack.Session, url string) (http.Header, error) {
+	return GetObjectMeta(session, url)
 }
 
 //DeleteContainer calls the OpenStack delete container API using
 //previously obtained token.
-func DeleteContainer(url, token string) error {
-	return DeleteObject(url, token)
+func DeleteContainer(session *openstack.Session, url string) error {
+	return DeleteObject(session, url)
 }
 
 //GetContainerMeta calls the OpenStack retrieve object metadata API
 //using previously obtained token.
 //url can be regular storage or CDN-enabled storage URL.
-func GetContainerMeta(url, token string) (http.Header, error) {
-	return GetObjectMeta(url, token)
+func GetContainerMeta(session *openstack.Session, url string) (http.Header, error) {
+	return GetObjectMeta(session, url)
 }
 
 //SetContainerMeta calls the OpenStack API to create / update meta data
 //for container using previously obtained token.
 //url can be regular storage or CDN-enabled storage URL.
-func SetContainerMeta(url string, token string, s ...string) (err error) {
-	return SetObjectMeta(url, token, s...)
+func SetContainerMeta(session *openstack.Session, url string, headers http.Header) (err error) {
+	return SetObjectMeta(session, url, headers)
 }
 
 //PutContainer calls the OpenStack API to create / update
 //container using previously obtained token.
-func PutContainer(url, token string, s ...string) error {
-	return PutObject(zeroByte, url, token, s...)
+func PutContainer(session *openstack.Session, url string, headers http.Header) error {
+	return PutObject(session, zeroByte, url, headers)
 }
 
 //ListObjects calls the OpenStack list object API using previously
 //obtained token. "Limit", "marker", "prefix", "path", "delim" corresponds
 //to the API's "limit", "marker", "prefix", "path", and "delimiter".
-func ListObjects(limit int64,
-	marker, prefix, path, delim, conURL, token string) ([]byte, error) {
-	var query = "?format=json"
+func ListObjects(session *openstack.Session, limit int64,
+	marker, prefix, path, delim, conURL string) ([]byte, error) {
+	var query url.Values = url.Values{}
+	query.Add("format", "json")
 	if limit > 0 {
-		query += "&limit=" + strconv.FormatInt(limit, 10)
+		query.Add("limit", strconv.FormatInt(limit, 10))
 	}
 	if marker != "" {
-		query += "&marker=" + url.QueryEscape(marker)
+		query.Add("marker", url.QueryEscape(marker))
 	}
 	if prefix != "" {
-		query += "&prefix=" + url.QueryEscape(prefix)
+		query.Add("prefix", url.QueryEscape(prefix))
 	}
 	if path != "" {
-		query += "&path=" + url.QueryEscape(path)
+		query.Add("path", url.QueryEscape(path))
 	}
 	if delim != "" {
-		query += "&delimiter=" + url.QueryEscape(delim)
+		query.Add("delimiter", url.QueryEscape(delim))
 	}
-	resp, err := util.CallAPI("GET", conURL+query, zeroByte,
-		"X-Auth-Token", token)
+	resp, err := session.Get(conURL, &query, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -107,23 +107,21 @@ func ListObjects(limit int64,
 //PutObject calls the OpenStack create object API using previously
 //obtained token.
 //url can be regular storage or CDN-enabled storage URL.
-func PutObject(fContent *[]byte, url, token string, s ...string) (err error) {
-	var headers http.Header = http.Header{}
-	headers.Set("X-Auth-Token", token)
-	resp, err := openstack.Put(url, nil, &headers, fContent)
+func PutObject(session *openstack.Session, fContent *[]byte, url string, headers http.Header) (err error) {
+	resp, err := session.Put(url, nil, &headers, fContent)
 	if err != nil {
 		return err
 	}
-	return util.CheckHTTPResponseStatusCode(resp.Resp)
+	return util.CheckHTTPResponseStatusCode(resp)
 }
 
 //CopyObject calls the OpenStack copy object API using previously obtained
 //token.  Note from API doc: "The destination container must exist before
 //attempting the copy."
-func CopyObject(srcURL, destURL, token string) (err error) {
-	resp, err := util.CallAPI("COPY", srcURL, zeroByte,
-		"X-Auth-Token", token,
-		"Destination", destURL)
+func CopyObject(session *openstack.Session, srcURL, destURL string) (err error) {
+	var headers http.Header = http.Header{}
+	headers.Add("Destination", destURL)
+	resp, err := session.Request("COPY", srcURL, nil, &headers, zeroByte)
 	if err != nil {
 		return err
 	}
@@ -138,8 +136,8 @@ func CopyObject(srcURL, destURL, token string) (err error) {
 //from the non-current container to the current." .. "If you want to completely
 //remove an object and you have five total versions of it, you must DELETE it
 //five times."
-func DeleteObject(url, token string) (err error) {
-	resp, err := util.CallAPI("DELETE", url, zeroByte, "X-Auth-Token", token)
+func DeleteObject(session *openstack.Session, url string) (err error) {
+	resp, err := session.Delete(url, nil, nil)
 	if err != nil {
 		return err
 	}
@@ -148,10 +146,9 @@ func DeleteObject(url, token string) (err error) {
 
 //SetObjectMeta calls the OpenStack API to create/update meta data for
 //object using previously obtained token.
-func SetObjectMeta(url string, token string, s ...string) (err error) {
-	s = append(s, "X-Auth-Token")
-	s = append(s, token)
-	resp, err := util.CallAPI("POST", url, zeroByte, s...)
+func SetObjectMeta(session *openstack.Session, url string, headers http.Header) (err error) {
+	// headers.Add("X-Auth-Token", token)
+	resp, err := session.Post(url, nil, &headers, zeroByte)
 	if err != nil {
 		return err
 	}
@@ -160,8 +157,8 @@ func SetObjectMeta(url string, token string, s ...string) (err error) {
 
 //GetObjectMeta calls the OpenStack retrieve object metadata API using
 //previously obtained token.
-func GetObjectMeta(url, token string) (http.Header, error) {
-	resp, err := util.CallAPI("HEAD", url, zeroByte, "X-Auth-Token", token)
+func GetObjectMeta(session *openstack.Session, url string) (http.Header, error) {
+	resp, err := session.Head(url, nil, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -175,8 +172,8 @@ func GetObjectMeta(url, token string) (http.Header, error) {
 //Since this implementation of GetObject retrieves header info, it
 //effectively executes GetObjectMeta also in addition to getting the
 //object content.
-func GetObject(url, token string) (http.Header, []byte, error) {
-	resp, err := util.CallAPI("GET", url, zeroByte, "X-Auth-Token", token)
+func GetObject(session *openstack.Session, url string) (http.Header, []byte, error) {
+	resp, err := session.Get(url, nil, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/openstack/README.md b/openstack/README.md
index e12d14f..fd60af1 100644
--- a/openstack/README.md
+++ b/openstack/README.md
@@ -2,3 +2,12 @@ openstack
 =========
 
 `openstack` is the API to an OpenStack cloud.
+
+* `session.go` - A Session object that encapsulates the HTTP REST handler
+  and authentication and logging
+
+* `auth.go` - The basic authentication interface
+
+* `auth-password.go` - Implements password authentication (v2 only at present)
+
+* `auth-token.go` - The returned token objects
diff --git a/openstack/auth-password.go b/openstack/auth-password.go
new file mode 100644
index 0000000..0c3b530
--- /dev/null
+++ b/openstack/auth-password.go
@@ -0,0 +1,81 @@
+// auth-password - Username/Password Authentication
+// Copyright 2015 Dean Troyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package openstack
+
+import (
+	"encoding/json"
+	"errors"
+	// "strings"
+)
+
+// The token request structure for Identity v2
+
+type PasswordCredentials struct {
+	Username string `json:"username"`
+	Password string `json:"password"`
+}
+
+type OSAuth struct {
+	PasswordCredentials `json:"passwordCredentials"`
+	Project             string `json:"tenantName"`
+}
+
+type UserPassV2 struct {
+	OSAuth  `json:"auth"`
+	AuthUrl string `json:"-"`
+}
+
+func NewUserPassV2(ao AuthOpts) (upv2 *UserPassV2, err error) {
+	// Validate incoming values
+	if ao.AuthUrl == "" {
+		err = errors.New("AuthUrl required")
+		return nil, err
+	}
+	if ao.Username == "" {
+		err = errors.New("Username required")
+		return nil, err
+	}
+	if ao.Password == "" {
+		err = errors.New("Password required")
+		return nil, err
+	}
+	upv2 = &UserPassV2{
+		AuthUrl: ao.AuthUrl,
+		OSAuth: OSAuth{
+			PasswordCredentials: PasswordCredentials{
+				Username: ao.Username,
+				Password: ao.Password,
+			},
+			Project: ao.Project,
+		},
+	}
+	return upv2, nil
+}
+
+// Produce JSON output
+func (s *UserPassV2) JSON() []byte {
+	reqAuth, err := json.Marshal(s)
+	if err != nil {
+		// Return an empty structure
+		reqAuth = []byte{'{', '}'}
+	}
+	return reqAuth
+}
+
+// func (self *UserPassV2) AuthUserPassV2(opts interface{}) (AuthRef, error) {
+//     auth, err := self.GetAuthRef()
+//     return AuthRef(auth), err
+// }
diff --git a/openstack/auth-token.go b/openstack/auth-token.go
new file mode 100644
index 0000000..48beb65
--- /dev/null
+++ b/openstack/auth-token.go
@@ -0,0 +1,81 @@
+// auth-token - Token Authentication
+// Copyright 2015 Dean Troyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package openstack
+
+import (
+	"errors"
+	"time"
+)
+
+// Identity Response Types
+
+type AccessType struct {
+	Token          Token                 `json:"token"`
+	User           interface{}           `json:"id"`
+	ServiceCatalog []ServiceCatalogEntry `json:"servicecatalog"`
+}
+
+type AuthToken struct {
+	Access AccessType `json:"access"`
+}
+
+type Token struct {
+	ID      string    `json:"id"`
+	Expires time.Time `json:"expires"`
+	Project struct {
+		ID   string `json:"id"`
+		Name string `json:"name"`
+	} `json:"tenant"`
+}
+
+type ServiceCatalogEntry struct {
+	Name      string            `json:"name"`
+	Type      string            `json:"type"`
+	Endpoints []ServiceEndpoint `json:"endpoints"`
+	// Endpoints []map[string]string `json:"endpoints"`
+}
+
+type ServiceEndpoint struct {
+	Type        string `json:"type"`
+	Region      string `json:"region"`
+	PublicURL   string `json:"publicurl"`
+	AdminURL    string `json:"adminurl"`
+	InternalURL string `json:"internalurl"`
+	VersionID   string `json:"versionid"`
+}
+
+func (s AuthToken) GetToken() string {
+	return s.Access.Token.ID
+}
+
+func (s AuthToken) GetExpiration() time.Time {
+	return s.Access.Token.Expires
+}
+
+func (s AuthToken) GetEndpoint(serviceType string, regionName string) (string, error) {
+
+	// Parse service catalog
+	for _, v := range s.Access.ServiceCatalog {
+		if v.Type == serviceType {
+			for _, r := range v.Endpoints {
+				if regionName == "" || r.Region == regionName {
+					return r.PublicURL, nil
+				}
+			}
+		}
+	}
+	return "", errors.New("err: endpoint not found")
+}
diff --git a/openstack/auth.go b/openstack/auth.go
new file mode 100644
index 0000000..63cf8e5
--- /dev/null
+++ b/openstack/auth.go
@@ -0,0 +1,99 @@
+// auth - Authentication interface
+// Copyright 2015 Dean Troyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package openstack
+
+import (
+	"encoding/json"
+	"errors"
+	"io/ioutil"
+	"strings"
+	"time"
+)
+
+// AuthRef is the returned authentication object, maybe v2 or v3
+type AuthRef interface {
+	GetToken() string
+	GetExpiration() time.Time
+	GetEndpoint(string, string) (string, error)
+}
+
+// AuthOpts is the set of credentials used to authenticate to OpenStack
+type AuthOpts struct {
+	// AuthUrl is always required
+	AuthUrl string
+
+	// Domain is ignored for v2 and required for v3 auth
+	Domain string
+
+	// Project is optional to get an unscoped token but required for
+	// a scoped token, which is required to do pretty much everything
+	// except list projects
+	Project string
+
+	// Username is required for password auth
+	Username string
+
+	// Password is required for password auth
+	Password string
+
+	// Token is required for Toekn auth
+	Token string
+}
+
+func (s *AuthOpts) GetAuthType() (string, error) {
+	var auth_type string
+	if s.AuthUrl != "" && s.Token != "" {
+		auth_type = "token"
+	} else if s.Username != "" {
+		auth_type = "password"
+	}
+	return auth_type, nil
+}
+
+// Basic auth call
+// These args should be an interface??
+func DoAuthRequest(authopts AuthOpts) (AuthRef, error) {
+	// url string, body []byte)
+	var auth = AuthToken{}
+
+	auth_mod, err := NewUserPassV2(authopts)
+	if err != nil {
+		err = errors.New("Failed to get auth options")
+		return nil, err
+	}
+
+	path := auth_mod.AuthUrl + "/tokens"
+	body := auth_mod.JSON()
+	resp, err := Post(path, nil, nil, &body)
+	if err != nil {
+		return nil, err
+	}
+
+	contentType := strings.ToLower(resp.Header.Get("Content-Type"))
+	if strings.Contains(contentType, "json") != true {
+		return nil, errors.New("err: header Content-Type is not JSON")
+	}
+
+	rbody, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, errors.New("aaa")
+	}
+	if err = json.Unmarshal(rbody, &auth); err != nil {
+		return nil, errors.New("bbb")
+	}
+
+	return auth, nil
+}
diff --git a/openstack/session.go b/openstack/session.go
index 235cabb..18a6ff2 100644
--- a/openstack/session.go
+++ b/openstack/session.go
@@ -19,12 +19,11 @@ import (
 	"bytes"
 	"crypto/tls"
 	"io"
-	"io/ioutil"
+	// "io/ioutil"
 	"log"
 	"net/http"
 	"net/http/httputil"
 	"net/url"
-	"strings"
 )
 
 var Debug = new(bool)
@@ -34,46 +33,28 @@ type Response struct {
 	Body []byte
 }
 
-type TokenInterface interface {
-	GetTokenId() string
-}
-
-type Token struct {
-	Expires string
-	Id      string
-	Project struct {
-		Id   string
-		Name string
-	}
-}
-
-func (t Token) GetTokenId() string {
-	return t.Id
-}
-
 // Generic callback to get a token from the auth plugin
-type AuthFunc func(s *Session, opts interface{}) (TokenInterface, error)
+type AuthFunc func(s *Session, opts interface{}) (AuthRef, error)
 
 type Session struct {
-	httpClient   *http.Client
-	endpoint     string
-	authenticate AuthFunc
-	Token        TokenInterface
-	Headers      http.Header
-	//	  ServCat map[string]ServiceEndpoint
+	httpClient *http.Client
+	AuthToken  AuthRef
+	Headers    http.Header
 }
 
-func NewSession(af AuthFunc, endpoint string, tls *tls.Config) (session *Session, err error) {
-	tr := &http.Transport{
-		TLSClientConfig:    tls,
-		DisableCompression: true,
+func NewSession(hclient *http.Client, auth AuthRef, tls *tls.Config) (session *Session, err error) {
+	if hclient == nil {
+		// Only build a transport if we're also building the client
+		tr := &http.Transport{
+			TLSClientConfig:    tls,
+			DisableCompression: true,
+		}
+		hclient = &http.Client{Transport: tr}
 	}
 	session = &Session{
-		// TODO(dtroyer): httpClient needs to be able to be passed in, or set externally
-		httpClient:   &http.Client{Transport: tr},
-		endpoint:     strings.TrimRight(endpoint, "/"),
-		authenticate: af,
-		Headers:      http.Header{},
+		httpClient: hclient,
+		AuthToken:  auth,
+		Headers:    http.Header{},
 	}
 	return session, nil
 }
@@ -83,47 +64,35 @@ func (s *Session) NewRequest(method, url string, headers *http.Header, body io.R
 	if err != nil {
 		return nil, err
 	}
-	// add token, get one if needed
-	if s.Token == nil && s.authenticate != nil {
-		var tok TokenInterface
-		tok, err = s.authenticate(s, nil)
-		if err != nil {
-			// (re-)auth failure!!
-			return nil, err
-		}
-		s.Token = tok
-	}
 	if headers != nil {
 		req.Header = *headers
 	}
-	if s.Token != nil {
-		req.Header.Add("X-Auth-Token", s.Token.GetTokenId())
+	if s.AuthToken != nil {
+		req.Header.Add("X-Auth-Token", s.AuthToken.GetToken())
 	}
 	return
 }
 
-func (s *Session) Do(req *http.Request) (*Response, error) {
-	if *Debug {
-		d, _ := httputil.DumpRequestOut(req, true)
-		log.Printf(">>>>>>>>>> REQUEST:\n", string(d))
-	}
-
+func (s *Session) Do(req *http.Request) (*http.Response, error) {
 	// Add session headers
 	for k := range s.Headers {
 		req.Header.Set(k, s.Headers.Get(k))
 	}
 
-	hresp, err := s.httpClient.Do(req)
+	if *Debug {
+		d, _ := httputil.DumpRequestOut(req, true)
+		log.Printf(">>>>>>>>>> REQUEST:\n", string(d))
+	}
+
+	resp, err := s.httpClient.Do(req)
 	if err != nil {
 		return nil, err
 	}
 	if *Debug {
-		dr, _ := httputil.DumpResponse(hresp, true)
+		dr, _ := httputil.DumpResponse(resp, true)
 		log.Printf("<<<<<<<<<< RESULT:\n", string(dr))
 	}
 
-	resp := new(Response)
-	resp.Resp = hresp
 	return resp, nil
 }
 
@@ -134,7 +103,7 @@ func (s *Session) Request(
 	params *url.Values,
 	headers *http.Header,
 	body *[]byte,
-) (resp *Response, err error) {
+) (resp *http.Response, err error) {
 	// add params to url here
 	if params != nil {
 		url = url + "?" + params.Encode()
@@ -151,35 +120,42 @@ func (s *Session) Request(
 		return nil, err
 	}
 
-	req.Header.Set("Content-Type", "application/json")
+	req.Header.Set("Accept", "application/json")
 
 	resp, err = s.Do(req)
 	if err != nil {
 		return nil, err
 	}
-	// do we need to parse this in this func? yes...
-	defer resp.Resp.Body.Close()
-
-	resp.Body, err = ioutil.ReadAll(resp.Resp.Body)
-	if err != nil {
-		return nil, err
-	}
 
 	return resp, nil
 }
 
+func (s *Session) Delete(
+	url string,
+	params *url.Values,
+	headers *http.Header) (resp *http.Response, err error) {
+	return s.Request("DELETE", url, params, headers, nil)
+}
+
 func (s *Session) Get(
 	url string,
 	params *url.Values,
-	headers *http.Header) (resp *Response, err error) {
+	headers *http.Header) (resp *http.Response, err error) {
 	return s.Request("GET", url, params, headers, nil)
 }
 
+func (s *Session) Head(
+	url string,
+	params *url.Values,
+	headers *http.Header) (resp *http.Response, err error) {
+	return s.Request("HEAD", url, params, headers, nil)
+}
+
 func (s *Session) Post(
 	url string,
 	params *url.Values,
 	headers *http.Header,
-	body *[]byte) (resp *Response, err error) {
+	body *[]byte) (resp *http.Response, err error) {
 	return s.Request("POST", url, params, headers, body)
 }
 
@@ -187,7 +163,7 @@ func (s *Session) Put(
 	url string,
 	params *url.Values,
 	headers *http.Header,
-	body *[]byte) (resp *Response, err error) {
+	body *[]byte) (resp *http.Response, err error) {
 	return s.Request("PUT", url, params, headers, body)
 }
 
@@ -195,8 +171,8 @@ func (s *Session) Put(
 func Get(
 	url string,
 	params *url.Values,
-	headers *http.Header) (resp *Response, err error) {
-	s, _ := NewSession(nil, "", nil)
+	headers *http.Header) (resp *http.Response, err error) {
+	s, _ := NewSession(nil, nil, nil)
 	return s.Get(url, params, headers)
 }
 
@@ -205,8 +181,8 @@ func Post(
 	url string,
 	params *url.Values,
 	headers *http.Header,
-	body *[]byte) (resp *Response, err error) {
-	s, _ := NewSession(nil, "", nil)
+	body *[]byte) (resp *http.Response, err error) {
+	s, _ := NewSession(nil, nil, nil)
 	return s.Post(url, params, headers, body)
 }
 
@@ -215,7 +191,7 @@ func Put(
 	url string,
 	params *url.Values,
 	headers *http.Header,
-	body *[]byte) (resp *Response, err error) {
-	s, _ := NewSession(nil, "", nil)
+	body *[]byte) (resp *http.Response, err error) {
+	s, _ := NewSession(nil, nil, nil)
 	return s.Put(url, params, headers, body)
 }
diff --git a/openstack/session_test.go b/openstack/session_test.go
index 7c172b8..0b5ee91 100644
--- a/openstack/session_test.go
+++ b/openstack/session_test.go
@@ -13,48 +13,47 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-
 package openstack_test
 
 import (
-    "encoding/json"
-    "net/http"
-    "testing"
+	"encoding/json"
+	"net/http"
+	"testing"
 
-    "git.openstack.org/stackforge/golang-client.git/openstack"
-    "git.openstack.org/stackforge/golang-client.git/testUtil"
+	"git.openstack.org/stackforge/golang-client.git/openstack"
+	"git.openstack.org/stackforge/golang-client.git/testUtil"
 )
 
 type TestStruct struct {
-    ID   string `json:"id"`
-    Name string `json:"name"`
+	ID   string `json:"id"`
+	Name string `json:"name"`
 }
 
 func TestSessionGet(t *testing.T) {
-    tokn := "eaaafd18-0fed-4b3a-81b4-663c99ec1cbb"
-    var apiServer = testUtil.CreateGetJsonTestServer(
-        t,
-        tokn,
-        `{"id":"id1","name":"Chris"}`,
-        nil,
-    )
-    expected := TestStruct{ID: "id1", Name: "Chris"}
-    actual := TestStruct{}
+	tokn := "eaaafd18-0fed-4b3a-81b4-663c99ec1cbb"
+	var apiServer = testUtil.CreateGetJsonTestServer(
+		t,
+		tokn,
+		`{"id":"id1","name":"Chris"}`,
+		nil,
+	)
+	expected := TestStruct{ID: "id1", Name: "Chris"}
+	actual := TestStruct{}
 
-    s, _ := openstack.NewSession(nil, "", nil)
-    var headers http.Header = http.Header{}
-    headers.Set("X-Auth-Token", tokn)
-    headers.Set("Accept", "application/json")
-    headers.Set("Etag", "md5hash-blahblah")
-    resp, err := s.Get(apiServer.URL, nil, &headers)
-    if err != nil {
-        t.Error(err)
-    }
-    testUtil.IsNil(t, err)
+	s, _ := openstack.NewSession(nil, "", nil)
+	var headers http.Header = http.Header{}
+	headers.Set("X-Auth-Token", tokn)
+	headers.Set("Accept", "application/json")
+	headers.Set("Etag", "md5hash-blahblah")
+	resp, err := s.Get(apiServer.URL, nil, &headers)
+	if err != nil {
+		t.Error(err)
+	}
+	testUtil.IsNil(t, err)
 
-    if err = json.Unmarshal(resp.Body, &actual); err != nil {
-        t.Error(err)
-    }
+	if err = json.Unmarshal(resp.Body, &actual); err != nil {
+		t.Error(err)
+	}
 
-    testUtil.Equals(t, expected, actual)
+	testUtil.Equals(t, expected, actual)
 }
diff --git a/testUtil/testUtil.go b/testUtil/testUtil.go
index 720486b..ad48a53 100644
--- a/testUtil/testUtil.go
+++ b/testUtil/testUtil.go
@@ -61,24 +61,24 @@ func IsNil(tb testing.TB, act interface{}) {
 // JSON Get requests. Takes a token, JSON payload, and a verification function
 // to do additional validation
 func CreateGetJsonTestServer(
-    t *testing.T,
-    expectedAuthToken string,
-    jsonResponsePayload string,
-    verifyRequest func(*http.Request)) *httptest.Server {
-    return httptest.NewServer(http.HandlerFunc(
-        func(w http.ResponseWriter, r *http.Request) {
-            headerValuesEqual(t, r, "X-Auth-Token", expectedAuthToken)
-            headerValuesEqual(t, r, "Accept", "application/json")
-            // verifyRequest(r)
-            if r.Method == "GET" {
-                w.Header().Set("Content-Type", "application/json")
-                w.Write([]byte(jsonResponsePayload))
-                w.WriteHeader(http.StatusOK)
-                return
-            }
+	t *testing.T,
+	expectedAuthToken string,
+	jsonResponsePayload string,
+	verifyRequest func(*http.Request)) *httptest.Server {
+	return httptest.NewServer(http.HandlerFunc(
+		func(w http.ResponseWriter, r *http.Request) {
+			headerValuesEqual(t, r, "X-Auth-Token", expectedAuthToken)
+			headerValuesEqual(t, r, "Accept", "application/json")
+			// verifyRequest(r)
+			if r.Method == "GET" {
+				w.Header().Set("Content-Type", "application/json")
+				w.Write([]byte(jsonResponsePayload))
+				w.WriteHeader(http.StatusOK)
+				return
+			}
 
-            t.Error(errors.New("Failed: r.Method == GET"))
-        }))
+			t.Error(errors.New("Failed: r.Method == GET"))
+		}))
 }
 
 // CreateGetJSONTestRequestServer creates a httptest.Server that can be used to test GetJson requests. Just specify the token,