There is a double pointer in both. Look at these two analogous lines from the two functions:
*head=temp;  // from insertAtTop
head->next=temp; // from insertAtLast
Note that head->next = temp can be equivalently written as (*head).next = temp.
There is a "double pointer" there: head is a pointer to a structure, and that structure contains a pointer next. Basically these two pictures:
Diagram for *head = temp:
head --> [ *--]---> old_head
            ^
            `-- temp is stored here, replacing the old_head
Diagram for head->next = temp, or (*head).next = temp. For the sake of example, the node structure is represented as three memory cells, the third of which is the next field. The exact memory details depend on your compiler and how you defined the structure!
head  --> [    ]
          [    ] 
          [ *--]--> old_next
             ^
             `-- temp is stored here, replacing old_next
Basically, the only difference is that you we have a structure instead of a just a single pointer object.        
In both cases, a pointer is being updated to refer to the newly inserted node. In the insertAtTop case, we have to replace that very pointer which represents the list itself. We are representing a list object as a pointer to the first node. If the list is changed so that it has a different first node, that pointer has to change. When inserting elsewhere in the list, we must update the previous node's next pointer to point to the new node, so we assign to (*head).next.
We could rewrite insertAtLast to use insertAtTop as a subroutine:
node* insertAtLast(node* head, int value){
    /* find the insertion point */
    node* original_head = head;
    while(head->next!=NULL){
        head=head->next;
    }
    /* now treat that as the head */
    (void) insertAtTop(&head->next, value);
    return original_head;
}
If you trace what happens in the call to insertAtHead, you will find that it does exactly the same thing as the code which we replaced. We took out this code:
    node* new_node=new node();
    new_node->data=value;
    new_node->next=NULL;
    h->next=new_node;
But that's almost the same as the body of insertAtHead which is this:
   node* temp=new node();
   temp->data=value;
   temp->next=*head;
   *head=temp;
We pass the value of the expression &h->next as the head parameter. So temp->next = *head is really temp->next = *&h->next where the *& cancel out, leaving temp->next = h->next.  But we know that h->next is NULL: our while loop has ensured that. So this is temp->next = NULL, effectively.
And of course *head = temp is the  same as *(&h->next) = temp, which is h->next = temp.