|
- package openid
-
- import (
- "errors"
- "io"
- "io/ioutil"
- "strings"
-
- "golang.org/x/net/html"
- )
-
- var yadisHeaders = map[string]string{
- "Accept": "application/xrds+xml"}
-
- func yadisDiscovery(id string, getter httpGetter) (opEndpoint string, opLocalID string, err error) {
- // Section 6.2.4 of Yadis 1.0 specifications.
- // The Yadis Protocol is initiated by the Relying Party Agent
- // with an initial HTTP request using the Yadis URL.
-
- // This request MUST be either a GET or a HEAD request.
-
- // A GET or HEAD request MAY include an HTTP Accept
- // request-header (HTTP 14.1) specifying MIME media type,
- // application/xrds+xml.
- resp, err := getter.Get(id, yadisHeaders)
- if err != nil {
- return "", "", err
- }
-
- defer resp.Body.Close()
-
- // Section 6.2.5 from Yadis 1.0 spec: Response
-
- contentType := resp.Header.Get("Content-Type")
-
- // The response MUST be one of:
- // (see 6.2.6 for precedence)
- if l := resp.Header.Get("X-XRDS-Location"); l != "" {
- // 2. HTTP response-headers that include an X-XRDS-Location
- // response-header, together with a document
- return getYadisResourceDescriptor(l, getter)
- } else if strings.Contains(contentType, "text/html") {
- // 1. An HTML document with a <head> element that includes a
- // <meta> element with http-equiv attribute, X-XRDS-Location,
-
- metaContent, err := findMetaXrdsLocation(resp.Body)
- if err == nil {
- return getYadisResourceDescriptor(metaContent, getter)
- }
- return "", "", err
- } else if strings.Contains(contentType, "application/xrds+xml") {
- // 4. A document of MIME media type, application/xrds+xml.
- body, err := ioutil.ReadAll(resp.Body)
- if err == nil {
- return parseXrds(body)
- }
- return "", "", err
- }
- // 3. HTTP response-headers only, which MAY include an
- // X-XRDS-Location response-header, a content-type
- // response-header specifying MIME media type,
- // application/xrds+xml, or both.
- // (this is handled by one of the 2 previous if statements)
- return "", "", errors.New("No expected header, or content type")
- }
-
- // Similar as above, but we expect an absolute Yadis document URL.
- func getYadisResourceDescriptor(id string, getter httpGetter) (opEndpoint string, opLocalID string, err error) {
- resp, err := getter.Get(id, yadisHeaders)
- if err != nil {
- return "", "", err
- }
- defer resp.Body.Close()
- // 4. A document of MIME media type, application/xrds+xml.
- body, err := ioutil.ReadAll(resp.Body)
- if err == nil {
- return parseXrds(body)
- }
- return "", "", err
- }
-
- // Search for
- // <head>
- // <meta http-equiv="X-XRDS-Location" content="....">
- func findMetaXrdsLocation(input io.Reader) (location string, err error) {
- tokenizer := html.NewTokenizer(input)
- inHead := false
- for {
- tt := tokenizer.Next()
- switch tt {
- case html.ErrorToken:
- return "", tokenizer.Err()
- case html.StartTagToken, html.EndTagToken:
- tk := tokenizer.Token()
- if tk.Data == "head" {
- if tt == html.StartTagToken {
- inHead = true
- } else {
- return "", errors.New("Meta X-XRDS-Location not found")
- }
- } else if inHead && tk.Data == "meta" {
- ok := false
- content := ""
- for _, attr := range tk.Attr {
- if attr.Key == "http-equiv" &&
- strings.ToLower(attr.Val) == "x-xrds-location" {
- ok = true
- } else if attr.Key == "content" {
- content = attr.Val
- }
- }
- if ok && len(content) > 0 {
- return content, nil
- }
- }
- }
- }
- return "", errors.New("Meta X-XRDS-Location not found")
- }
|