You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

77 lines
2.0 KiB

  1. package openid
  2. import (
  3. "errors"
  4. "io"
  5. "golang.org/x/net/html"
  6. )
  7. func htmlDiscovery(id string, getter httpGetter) (opEndpoint, opLocalID, claimedID string, err error) {
  8. resp, err := getter.Get(id, nil)
  9. if err != nil {
  10. return "", "", "", err
  11. }
  12. opEndpoint, opLocalID, err = findProviderFromHeadLink(resp.Body)
  13. return opEndpoint, opLocalID, resp.Request.URL.String(), err
  14. }
  15. func findProviderFromHeadLink(input io.Reader) (opEndpoint, opLocalID string, err error) {
  16. tokenizer := html.NewTokenizer(input)
  17. inHead := false
  18. for {
  19. tt := tokenizer.Next()
  20. switch tt {
  21. case html.ErrorToken:
  22. // Even if the document is malformed after we found a
  23. // valid <link> tag, ignore and let's be happy with our
  24. // openid2.provider and potentially openid2.local_id as well.
  25. if len(opEndpoint) > 0 {
  26. return
  27. }
  28. return "", "", tokenizer.Err()
  29. case html.StartTagToken, html.EndTagToken, html.SelfClosingTagToken:
  30. tk := tokenizer.Token()
  31. if tk.Data == "head" {
  32. if tt == html.StartTagToken {
  33. inHead = true
  34. } else {
  35. if len(opEndpoint) > 0 {
  36. return
  37. }
  38. return "", "", errors.New(
  39. "LINK with rel=openid2.provider not found")
  40. }
  41. } else if inHead && tk.Data == "link" {
  42. provider := false
  43. localID := false
  44. href := ""
  45. for _, attr := range tk.Attr {
  46. if attr.Key == "rel" {
  47. if attr.Val == "openid2.provider" {
  48. provider = true
  49. } else if attr.Val == "openid2.local_id" {
  50. localID = true
  51. } else if attr.Val == "openid.server" {
  52. provider = true
  53. }
  54. } else if attr.Key == "href" {
  55. href = attr.Val
  56. }
  57. }
  58. if provider && !localID && len(href) > 0 {
  59. opEndpoint = href
  60. } else if !provider && localID && len(href) > 0 {
  61. opLocalID = href
  62. }
  63. }
  64. }
  65. }
  66. // At this point we should probably have returned either from
  67. // a closing </head> or a tokenizer error (no </head> found).
  68. // But just in case.
  69. if len(opEndpoint) > 0 {
  70. return
  71. }
  72. return "", "", errors.New("LINK rel=openid2.provider not found")
  73. }