Skip to content

Instantly share code, notes, and snippets.

@alfredwesterveld
Created January 17, 2011 21:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alfredwesterveld/9742de21ca5f121deab9 to your computer and use it in GitHub Desktop.
Save alfredwesterveld/9742de21ca5f121deab9 to your computer and use it in GitHub Desktop.
Lightopenid even lighter below 10KB
<?php
class LightOpenID {
public $returnUrl, $required = array( ), $optional = array( ), $verify_peer = null, $capath = null, $cainfo = null;
private $identity, $claimed_id;
protected $server, $version, $trustRoot, $aliases, $identifier_select = false, $ax = false, $sreg = false, $data;
function __construct( ) {
$this->trustRoot = ( !empty( $_SERVER[ 'HTTPS' ] ) ? 'https' : 'http' ) . '://' . $_SERVER[ 'HTTP_HOST' ];
$uri = rtrim( preg_replace( '#((?<=\?)|&)openid\.[^&]+#', '', $_SERVER[ 'REQUEST_URI' ] ), '?' );
$this->returnUrl = $this->trustRoot . $uri;
$this->data = $_POST + $_GET;
}
function __set( $name, $value ) {
switch ( $name ) {
case 'identity':
if ( strlen( $value = trim( (String) $value ) ) ) {
if ( preg_match( '#^xri:/*#i', $value, $m ) )
$value = substr( $value, strlen( $m[ 0 ] ) );
elseif ( !preg_match( '/^(?:[=@+\$!\(]|https?:)/i', $value ) )
$value = "http://$value";
if ( preg_match( '#^https?://[^/]+$#i', $value, $m ) )
$value .= '/';
}
$this->$name = $this->claimed_id = $value;
break;
case 'realm':
$this->trustRoot = trim( $value );
}
}
function __get( $name ) {
switch ( $name ) {
case 'identity':
return $this->claimed_id;
case 'trustRoot':
case 'realm':
return $this->trustRoot;
case 'mode':
return empty( $this->data[ 'openid_mode' ] ) ? null : $this->data[ 'openid_mode' ];
}
}
protected function request_curl( $url, $method = 'GET', $params = array( ) ) {
$params = http_build_query( $params, '', '&' );
$c = curl_init( $url . ( $method == 'GET' && $params ? '?' . $params : '' ) );
curl_setopt( $c, CURLOPT_FOLLOWLOCATION, true );
curl_setopt( $c, CURLOPT_HEADER, false );
curl_setopt( $c, CURLOPT_SSL_VERIFYPEER, false );
curl_setopt( $c, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $c, CURLOPT_HTTPHEADER, array(
'Accept: application/xrds+xml, */*'
) );
if ( $this->verify_peer !== null ) {
curl_setopt( $c, CURLOPT_SSL_VERIFYPEER, $this->verify_peer );
if ( $this->capath )
curl_setopt( $c, CURLOPT_CAPATH, $this->capath );
if ( $this->cainfo )
curl_setopt( $c, CURLOPT_CAINFO, $this->cainfo );
}
if ( $method == 'POST' ) {
curl_setopt( $c, CURLOPT_POST, true );
curl_setopt( $c, CURLOPT_POSTFIELDS, $params );
} elseif ( $method == 'HEAD' ) {
curl_setopt( $c, CURLOPT_HEADER, true );
curl_setopt( $c, CURLOPT_NOBODY, true );
} else
curl_setopt( $c, CURLOPT_HTTPGET, true );
$res = curl_exec( $c );
if ( $method == 'HEAD' ) {
$headers = array( );
foreach ( explode( "\n", $res ) as $header ) {
$pos = strpos( $header, ':' );
$name = strtolower( trim( substr( $header, 0, $pos ) ) );
$headers[ $name ] = trim( substr( $header, $pos + 1 ) );
}
$effective_url = curl_getinfo( $c, CURLINFO_EFFECTIVE_URL );
if ( $effective_url != $url )
$this->identity = $this->claimed_id = $effective_url;
return $headers;
}
if ( curl_errno( $c ) )
throw new ErrorException( curl_error( $c ), curl_errno( $c ) );
return $res;
}
protected function request( $url, $method = 'GET', $params = array( ) ) {
return $this->request_curl( $url, $method, $params );
}
protected function build_url( $url, $parts ) {
if ( isset( $url[ 'query' ], $parts[ 'query' ] ) )
$parts[ 'query' ] = $url[ 'query' ] . '&' . $parts[ 'query' ];
$url = $parts + $url;
$url = $url[ 'scheme' ] . '://' . ( empty( $url[ 'username' ] ) ? '' : ( empty( $url[ 'password' ] ) ? "{$url['username']}@" : "{$url['username']}:{$url['password']}@" ) ) . $url[ 'host' ] . ( empty( $url[ 'port' ] ) ? '' : ":{$url['port']}" ) . ( empty( $url[ 'path' ] ) ? '' : $url[ 'path' ] ) . ( empty( $url[ 'query' ] ) ? '' : "?{$url['query']}" ) . ( empty( $url[ 'fragment' ] ) ? '' : "#{$url['fragment']}" );
return $url;
}
protected function htmlTag( $content, $tag, $attrName, $attrValue, $valueName ) {
preg_match_all( "#<{$tag}[^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*$valueName=['\"](.+?)['\"][^>]*/?>#i", $content, $matches1 );
preg_match_all( "#<{$tag}[^>]*$valueName=['\"](.+?)['\"][^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*/?>#i", $content, $matches2 );
$result = array_merge( $matches1[ 1 ], $matches2[ 1 ] );
return empty( $result ) ? false : $result[ 0 ];
}
function discover( $url ) {
if ( !$url )
throw new ErrorException( 'No identity supplied.' );
if ( !preg_match( '#^https?:#', $url ) )
$url = "https://xri.net/$url";
$originalUrl = $url;
$yadis = true;
for ( $i = 0; $i < 5; $i++ ) {
if ( $yadis ) {
$headers = $this->request( $url, 'HEAD' );
$next = false;
if ( isset( $headers[ 'x-xrds-location' ] ) ) {
$url = $this->build_url( parse_url( $url ), parse_url( trim( $headers[ 'x-xrds-location' ] ) ) );
$next = true;
}
if ( isset( $headers[ 'content-type' ] ) && ( strpos( $headers[ 'content-type' ], 'application/xrds+xml' ) !== false || strpos( $headers[ 'content-type' ], 'text/xml' ) !== false ) ) {
$content = $this->request( $url, 'GET' );
preg_match_all( '#<Service.*?>(.*?)</Service>#s', $content, $m );
foreach ( $m[ 1 ] as $content ) {
$content = ' ' . $content;
$ns = preg_quote( 'http://specs.openid.net/auth/2.0/' );
if ( preg_match( '#<Type>\s*' . $ns . '(server|signon)\s*</Type>#s', $content, $type ) ) {
if ( $type[ 1 ] == 'server' )
$this->identifier_select = true;
preg_match( '#<URI.*?>(.*)</URI>#', $content, $server );
preg_match( '#<(Local|Canonical)ID>(.*)</\1ID>#', $content, $delegate );
if ( empty( $server ) )
return false;
$this->ax = (bool) strpos( $content, '<Type>http://openid.net/srv/ax/1.0</Type>' );
$this->sreg = strpos( $content, '<Type>http://openid.net/sreg/1.0</Type>' ) || strpos( $content, '<Type>http://openid.net/extensions/sreg/1.1</Type>' );
$server = $server[ 1 ];
if ( isset( $delegate[ 2 ] ) )
$this->identity = trim( $delegate[ 2 ] );
$this->version = 2;
$this->server = $server;
return $server;
}
}
}
}
}
throw new ErrorException( 'Endless redirection!' );
}
protected function axParams( ) {
$params = array( );
if ( $this->required || $this->optional ) {
$params[ 'openid.ns.ax' ] = 'http://openid.net/srv/ax/1.0';
$params[ 'openid.ax.mode' ] = 'fetch_request';
$this->aliases = array( );
$counts = array( );
$required = array( );
$optional = array( );
foreach ( array(
'required',
'optional'
) as $type ) {
foreach ( $this->$type as $alias => $field ) {
if ( is_int( $alias ) )
$alias = strtr( $field, '/', '_' );
$this->aliases[ $alias ] = 'http://axschema.org/' . $field;
if ( empty( $counts[ $alias ] ) )
$counts[ $alias ] = 0;
$counts[ $alias ] += 1;
${$type}[ ] = $alias;
}
}
foreach ( $this->aliases as $alias => $ns )
$params[ 'openid.ax.type.' . $alias ] = $ns;
foreach ( $counts as $alias => $count )
if ( $count == 1 )
continue;
$params[ 'openid.ax.count.' . $alias ] = $count;
if ( $required )
$params[ 'openid.ax.required' ] = implode( ',', $required );
if ( $optional )
$params[ 'openid.ax.if_available' ] = implode( ',', $optional );
}
return $params;
}
function validate( ) {
$this->claimed_id = isset( $this->data[ 'openid_claimed_id' ] ) ? $this->data[ 'openid_claimed_id' ] : $this->data[ 'openid_identity' ];
$params = array(
'openid.assoc_handle' => $this->data[ 'openid_assoc_handle' ],
'openid.signed' => $this->data[ 'openid_signed' ],
'openid.sig' => $this->data[ 'openid_sig' ]
);
if ( isset( $this->data[ 'openid_ns' ] ) )
$params[ 'openid.ns' ] = 'http://specs.openid.net/auth/2.0';
elseif ( isset( $this->data[ 'openid_claimed_id' ] ) && $this->data[ 'openid_claimed_id' ] != $this->data[ 'openid_identity' ] )
$this->returnUrl .= ( strpos( $this->returnUrl, '?' ) ? '&' : '?' ) . 'openid.claimed_id=' . $this->claimed_id;
if ( $this->data[ 'openid_return_to' ] != $this->returnUrl )
return false;
$server = $this->discover( $this->claimed_id );
foreach ( explode( ',', $this->data[ 'openid_signed' ] ) as $item ) {
$value = $this->data[ 'openid_' . str_replace( '.', '_', $item ) ];
$params[ 'openid.' . $item ] = get_magic_quotes_gpc() ? stripslashes( $value ) : $value;
}
$params[ 'openid.mode' ] = 'check_authentication';
$res = $this->request( $server, 'POST', $params );
return preg_match( '/is_valid\s*:\s*true/i', $res );
}
protected function authUrl_v2( $identifier_select ) {
$params = array(
'openid.ns' => 'http://specs.openid.net/auth/2.0',
'openid.mode' => 'checkid_setup',
'openid.return_to' => $this->returnUrl,
'openid.realm' => $this->trustRoot
);
if ( $this->ax )
$params += $this->axParams();
if ( !$this->ax && !$this->sreg )
$params += $this->axParams();
if ( $identifier_select )
$params[ 'openid.identity' ] = $params[ 'openid.claimed_id' ] = 'http://specs.openid.net/auth/2.0/identifier_select';
else {
$params[ 'openid.identity' ] = $this->identity;
$params[ 'openid.claimed_id' ] = $this->claimed_id;
}
return $this->build_url( parse_url( $this->server ), array(
'query' => http_build_query( $params, '', '&' )
) );
}
function authUrl( $identifier_select = null ) {
if ( !$this->server )
$this->discover( $this->identity );
if ( $this->version == 2 ) {
if ( $identifier_select === null ) {
return $this->authUrl_v2( $this->identifier_select );
}
return $this->authUrl_v2( $identifier_select );
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment